Merge "Add a new extension point SshExecuteCommandInterceptor"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index cebac23..aa45329 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1145,42 +1145,23 @@
 [[change]]
 === Section change
 
-[[change.largeChange]]change.largeChange::
-+
-Number of changed lines from which on a change is considered as a large
-change. The number of changed lines of a change is the sum of the lines
-that were inserted and deleted in the change.
-+
-The specified value is used to visualize the change sizes in the Web UI
-in change tables and user dashboards.
-+
-By default 500.
-
-[[change.updateDelay]]change.updateDelay::
-+
-How often in seconds the web interface should poll for updates to the
-currently open change.  The poller relies on the client's browser
-cache to use If-Modified-Since and respect `304 Not Modified` HTTP
-responses.  This allows for fast polls, often under 8 milliseconds.
-+
-With a configured 30 second delay a server with 4900 active users will
-typically need to dedicate 1 CPU to the update check.  4900 users
-divided by an average delay of 30 seconds is 163 requests arriving per
-second.  If requests are served at \~6 ms response time, 1 CPU is
-necessary to keep up with the update request traffic.  On a smaller
-user base of 500 active users, the default 30 second delay is only 17
-requests per second and requires ~10% CPU.
-+
-If 0 the update polling is disabled.
-+
-Default is 5 minutes.
-
 [[change.allowBlame]]change.allowBlame::
 +
 Allow blame on side by side diff. If set to false, blame cannot be used.
 +
 Default is true.
 
+[[change.allowDrafts]]change.allowDrafts::
++
+Legacy support for drafts workflow. If set to true, pushing a new change
+with draft option will create a private change. Pushing with draft option
+to an existing change will create change edit.
++
+Enabling this option allows to push to the `refs/drafts/branch`. When
+disabled any push to `refs/drafts/branch` will be rejected.
++
+Default is false.
+
 [[change.api.allowedIdentifier]]change.api.allowedIdentifier::
 +
 Change identifier(s) that are allowed on the API. See
@@ -1193,14 +1174,12 @@
 +
 Default is `ALL`.
 
-[[change.allowDrafts]]change.allowDrafts::
+[[change.api.excludeMergeableInChangeInfo]]change.api.excludeMergeableInChangeInfo::
 +
-Legacy support for drafts workflow. If set to true, pushing a new change
-with draft option will create a private change. Pushing with draft option
-to an existing change will create change edit.
-+
-Enabling this option allows to push to the `refs/drafts/branch`. When
-disabled any push to `refs/drafts/branch` will be rejected.
+If true, the mergeability bit in
+link:rest-api-changes.html#change-info[ChangeInfo] will never be set. It can
+be requested separately through the
+link:rest-api-changes.html#get-mergeable[get-mergeable] endpoint.
 +
 Default is false.
 
@@ -1219,14 +1198,47 @@
 +
 Default is true.
 
-[[change.api.excludeMergeableInChangeInfo]]change.api.excludeMergeableInChangeInfo::
+[[change.disablePrivateChanges]]change.disablePrivateChanges::
 +
-If true, the mergeability bit in
-link:rest-api-changes.html#change-info[ChangeInfo] will never be set. It can
-be requested separately through the
-link:rest-api-changes.html#get-mergeable[get-mergeable] endpoint.
+If set to true, users are not allowed to create private changes.
 +
-Default is false.
+The default is false.
+
+[[change.largeChange]]change.largeChange::
++
+Number of changed lines from which on a change is considered as a large
+change. The number of changed lines of a change is the sum of the lines
+that were inserted and deleted in the change.
++
+The specified value is used to visualize the change sizes in the Web UI
+in change tables and user dashboards.
++
+By default 500.
+
+[[change.replyLabel]]change.replyLabel::
++
+Label name for the reply button. In the user interface an ellipsis (…)
+is appended.
++
+Default is "Reply". In the user interface it becomes "Reply…".
+
+[[change.replyTooltip]]change.replyTooltip::
++
+Tooltip for the reply button. In the user interface a note about the
+keyboard shortcut is appended.
++
+Default is "Reply and score". In the user interface it becomes "Reply
+and score (Shortcut: a)".
+
+[[change.robotCommentSizeLimit]]change.robotCommentSizeLimit::
++
+Maximum allowed size of a robot comment that will be accepted. Robot comments
+which exceed the indicated size will be rejected on addition. The specified
+value is interpreted as the maximum size in bytes of the JSON representation of
+the robot comment. Common unit suffixes of 'k', 'm', or 'g' are supported.
+Zero or negative values allow robot comments of unlimited size.
++
+The default limit is 1024kB.
 
 [[change.showAssigneeInChangesTable]]change.showAssigneeInChangesTable::
 +
@@ -1235,6 +1247,14 @@
 +
 Default is false.
 
+[[change.strictLabels]]change.strictLabels::
++
+Reject invalid label votes: invalid labels or invalid values. This
+configuration option is provided for backwards compaitbility and may
+be removed in future gerrit versions.
++
+Default is false.
+
 [[change.submitLabel]]change.submitLabel::
 +
 Label name for the submit button.
@@ -1268,13 +1288,6 @@
 Default is "Submit all ${topicSize} changes of the same topic (${submitSize}
 changes including ancestors and other changes related by topic)".
 
-[[change.submitWholeTopic]]change.submitWholeTopic::
-+
-Determines if the submit button submits the whole topic instead of
-just the current change.
-+
-Default is false.
-
 [[change.submitTopicLabel]]change.submitTopicLabel::
 +
 If `change.submitWholeTopic` is set and a change has a topic,
@@ -1295,44 +1308,31 @@
 (${submitSize} changes including ancestors and other
 changes related by topic)".
 
-[[change.replyLabel]]change.replyLabel::
+[[change.submitWholeTopic]]change.submitWholeTopic::
 +
-Label name for the reply button. In the user interface an ellipsis (…)
-is appended.
-+
-Default is "Reply". In the user interface it becomes "Reply…".
-
-[[change.replyTooltip]]change.replyTooltip::
-+
-Tooltip for the reply button. In the user interface a note about the
-keyboard shortcut is appended.
-+
-Default is "Reply and score". In the user interface it becomes "Reply
-and score (Shortcut: a)".
-
-[[change.robotCommentSizeLimit]]change.robotCommentSizeLimit::
-+
-Maximum allowed size of a robot comment that will be accepted. Robot comments
-which exceed the indicated size will be rejected on addition. The specified
-value is interpreted as the maximum size in bytes of the JSON representation of
-the robot comment. Common unit suffixes of 'k', 'm', or 'g' are supported.
-Zero or negative values allow robot comments of unlimited size.
-+
-The default limit is 1024kB.
-
-[[change.strictLabels]]change.strictLabels::
-+
-Reject invalid label votes: invalid labels or invalid values. This
-configuration option is provided for backwards compaitbility and may
-be removed in future gerrit versions.
+Determines if the submit button submits the whole topic instead of
+just the current change.
 +
 Default is false.
 
-[[change.disablePrivateChanges]]change.disablePrivateChanges::
+[[change.updateDelay]]change.updateDelay::
 +
-If set to true, users are not allowed to create private changes.
+How often in seconds the web interface should poll for updates to the
+currently open change.  The poller relies on the client's browser
+cache to use If-Modified-Since and respect `304 Not Modified` HTTP
+responses.  This allows for fast polls, often under 8 milliseconds.
 +
-The default is false.
+With a configured 30 second delay a server with 4900 active users will
+typically need to dedicate 1 CPU to the update check.  4900 users
+divided by an average delay of 30 seconds is 163 requests arriving per
+second.  If requests are served at \~6 ms response time, 1 CPU is
+necessary to keep up with the update request traffic.  On a smaller
+user base of 500 active users, the default 30 second delay is only 17
+requests per second and requires ~10% CPU.
++
+If 0 the update polling is disabled.
++
+Default is 5 minutes.
 
 [[changeCleanup]]
 === Section changeCleanup
@@ -4769,7 +4769,7 @@
 
 +
 The time zone cannot be specified but is always the system default
-time zone.
+time zone. Hours must be zero-padded, i.e. `06:00` rather than `6:00`.
 
 The section (and optionally the subsection) in which the `interval` and
 `startTime` keys must be set depends on the background job for which a
diff --git a/Documentation/config-plugins.txt b/Documentation/config-plugins.txt
index 81c9927..8324a72 100644
--- a/Documentation/config-plugins.txt
+++ b/Documentation/config-plugins.txt
@@ -143,6 +143,18 @@
 rights directly to a single user, since in Gerrit access rights can
 only be assigned to groups.
 
+[[webhooks]]
+=== webhooks
+
+This plugin allows to propagate Gerrit events to remote http endpoints.
+
+link:https://gerrit-review.googlesource.com/admin/repos/plugins/webhooks[
+Project] |
+link:https://gerrit.googlesource.com/plugins/webhooks/+doc/master/src/main/resources/Documentation/about.md[
+Documentation] |
+link:https://gerrit.googlesource.com/plugins/webhooks/+doc/master/src/main/resources/Documentation/config.md[
+Configuration]
+
 [[other-plugins]]
 == Other Plugins
 
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 0990e57..20c9fa2 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -455,7 +455,7 @@
 ----
 import com.google.gerrit.common.EventDispatcher;
 import com.google.gerrit.extensions.registration.DynamicItem;
-import com.google.gwtorm.server.OrmException;
+import com.google.exceptions.StorageException;
 import com.google.inject.Inject;
 
 class MyPlugin {
@@ -469,7 +469,7 @@
   private void postEvent(MyPluginEvent event) {
     try {
       eventDispatcher.get().postEvent(event);
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       // error handling
     }
   }
@@ -720,7 +720,7 @@
 ----
 public class SampleOperator
     implements ChangeQueryBuilder.ChangeOperatorFactory {
-  public static class MyPredicate extends OperatorChangePredicate<ChangeData> {
+  public static class MyPredicate extends PostFilterPredicate<ChangeData> {
     ...
   }
 
diff --git a/Documentation/licenses.txt b/Documentation/licenses.txt
index 4f14bca..6e61e29 100644
--- a/Documentation/licenses.txt
+++ b/Documentation/licenses.txt
@@ -94,7 +94,6 @@
 * guava
 * guava-failureaccess
 * guava-retrying
-* gwtorm-client
 * html-types
 * j2objc
 * jsr305
diff --git a/Documentation/pg-plugin-dev.txt b/Documentation/pg-plugin-dev.txt
index e7e5f73..8fb5655 100644
--- a/Documentation/pg-plugin-dev.txt
+++ b/Documentation/pg-plugin-dev.txt
@@ -185,30 +185,6 @@
 
 Note: TODO
 
-=== registerApiHook
-`plugin.registerApiHook(apiEndpointName, addParameterFunction)`
-
-Registers a rest api hook to add plugin params to core rest api calls.
-The addParameterFunction only needs to return plugin specific params, not the
-entire params object.
-
-Supported endpoints are `changes` and `change`.
-
-```
-Gerrit.install('my-plugin-name', plugin => {
-  plugin.restApiHooks().registerRestApiParams('changes', params => {
-    return 'my-plugin-option';
-  });
-});
-
-// will update the changes query params to be:
-
-{
-  // existing changes params
-  'my-plugin-name': 'my-plugin-option',
-}
-```
-
 === registerStyleModule
 `plugin.registerStyleModule(endpointName, moduleName)`
 
diff --git a/Documentation/pg-plugin-endpoints.txt b/Documentation/pg-plugin-endpoints.txt
index cb80c74..ff62da1 100644
--- a/Documentation/pg-plugin-endpoints.txt
+++ b/Documentation/pg-plugin-endpoints.txt
@@ -35,6 +35,11 @@
 
 The following endpoints are available to plugins.
 
+=== banner
+The `banner` extension point is located at the top of all pages. The purpose
+is to allow plugins to show outage information and important announcements to
+all users.
+
 === change-view-integration
 The `change-view-integration` extension point is located between `Files` and
 `Messages` section on the change view page, and it may take full page's
diff --git a/WORKSPACE b/WORKSPACE
index d75bc19..2a0c1ec 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -14,9 +14,23 @@
 
 http_archive(
     name = "io_bazel_rules_closure",
-    sha256 = "ddce3b3a3909f99b28b25071c40b7fec7e2e1d1d1a4b2e933f3082aa99517105",
-    strip_prefix = "rules_closure-316e6133888bfc39fb860a4f1a31cfcbae485aef",
-    urls = ["https://github.com/bazelbuild/rules_closure/archive/316e6133888bfc39fb860a4f1a31cfcbae485aef.tar.gz"],
+    sha256 = "34abd9170fdbfdfc6f3b63f2c18cee3cbcb2ddbd5e3c97324add0aa7809ed875",
+    strip_prefix = "rules_closure-9d543facf886631e4ed379996e60ce3533188adc",
+    urls = ["https://github.com/bazelbuild/rules_closure/archive/9d543facf886631e4ed379996e60ce3533188adc.tar.gz"],
+)
+
+# Transitive dependency of rules_closure and protobuf
+http_archive(
+    name = "net_zlib",
+    build_file = "//:lib/zlib/BUILD",
+    sha256 = "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1",
+    strip_prefix = "zlib-1.2.11",
+    urls = ["https://zlib.net/zlib-1.2.11.tar.gz"],
+)
+
+bind(
+    name = "zlib",
+    actual = "@net_zlib//:zlib",
 )
 
 # File is specific to Polymer and copied from the Closure Github -- should be
@@ -177,16 +191,9 @@
 )
 
 maven_jar(
-    name = "gwtorm-client",
-    artifact = "com.google.gerrit:gwtorm:1.20",
-    sha1 = "a4809769b710bc8ce3f203125630b8419f0e58b0",
-    src_sha1 = "cb63296276ce3228b2d83a37017a99e38ad8ed42",
-)
-
-maven_jar(
     name = "protobuf",
-    artifact = "com.google.protobuf:protobuf-java:3.6.1",
-    sha1 = "0d06d46ecfd92ec6d0f3b423b4cd81cb38d8b924",
+    artifact = "com.google.protobuf:protobuf-java:3.7.1",
+    sha1 = "0bce1b6dc9e4531169542ab37a1c8641bcaa8afb",
 )
 
 load("//lib:guava.bzl", "GUAVA_BIN_SHA1", "GUAVA_VERSION")
@@ -221,30 +228,30 @@
     sha1 = "28c59f58f5adcc307604602e2aa89e2aca14c554",
 )
 
-SLF4J_VERS = "1.7.7"
+SLF4J_VERS = "1.7.26"
 
 maven_jar(
     name = "log-api",
     artifact = "org.slf4j:slf4j-api:" + SLF4J_VERS,
-    sha1 = "2b8019b6249bb05d81d3a3094e468753e2b21311",
+    sha1 = "77100a62c2e6f04b53977b9f541044d7d722693d",
 )
 
 maven_jar(
     name = "log-ext",
     artifact = "org.slf4j:slf4j-ext:" + SLF4J_VERS,
-    sha1 = "09a8f58c784c37525d2624062414358acf296717",
+    sha1 = "31cdf122e000322e9efcb38913e9ab07825b17ef",
 )
 
 maven_jar(
     name = "impl-log4j",
     artifact = "org.slf4j:slf4j-log4j12:" + SLF4J_VERS,
-    sha1 = "58f588119ffd1702c77ccab6acb54bfb41bed8bd",
+    sha1 = "12f5c685b71c3027fd28bcf90528ec4ec74bf818",
 )
 
 maven_jar(
     name = "jcl-over-slf4j",
     artifact = "org.slf4j:jcl-over-slf4j:" + SLF4J_VERS,
-    sha1 = "56003dcd0a31deea6391b9e2ef2f2dc90b205a92",
+    sha1 = "33fbc2d93de829fa5e263c5ce97f5eab8f57d53e",
 )
 
 maven_jar(
@@ -588,18 +595,18 @@
     sha1 = "18d4d07010c24405129a6dbb0e92057f8779fb9d",
 )
 
-AUTO_VALUE_VERSION = "1.6.3"
+AUTO_VALUE_VERSION = "1.6.5"
 
 maven_jar(
     name = "auto-value",
     artifact = "com.google.auto.value:auto-value:" + AUTO_VALUE_VERSION,
-    sha1 = "8edb6675b9c09ffdcc19937428e7ef1e3d066e12",
+    sha1 = "816872c85048f36a67a276ef7a49cc2e4595711c",
 )
 
 maven_jar(
     name = "auto-value-annotations",
     artifact = "com.google.auto.value:auto-value-annotations:" + AUTO_VALUE_VERSION,
-    sha1 = "b88c1bb7f149f6d2cc03898359283e57b08f39cc",
+    sha1 = "c3dad10377f0e2242c9a4b88e9704eaf79103679",
 )
 
 # Transitive dependency of commons-compress
@@ -741,8 +748,8 @@
 
 maven_jar(
     name = "dropwizard-core",
-    artifact = "io.dropwizard.metrics:metrics-core:4.0.3",
-    sha1 = "bb562ee73f740bb6b2bf7955f97be6b870d9e9f0",
+    artifact = "io.dropwizard.metrics:metrics-core:4.0.5",
+    sha1 = "b81ef162970cdb9f4512ee2da09715a856ff4c4c",
 )
 
 # When updating Bouncy Castle, also update it in bazlets.
@@ -822,15 +829,15 @@
 # elasticsearch-rest-client explicitly depends on this version
 maven_jar(
     name = "httpasyncclient",
-    artifact = "org.apache.httpcomponents:httpasyncclient:4.1.2",
-    sha1 = "95aa3e6fb520191a0970a73cf09f62948ee614be",
+    artifact = "org.apache.httpcomponents:httpasyncclient:4.1.4",
+    sha1 = "f3a3240681faae3fa46b573a4c7e50cec9db0d86",
 )
 
 # elasticsearch-rest-client explicitly depends on this version
 maven_jar(
     name = "httpcore-nio",
-    artifact = "org.apache.httpcomponents:httpcore-nio:4.4.5",
-    sha1 = "f4be009e7505f6ceddf21e7960c759f413f15056",
+    artifact = "org.apache.httpcomponents:httpcore-nio:4.4.11",
+    sha1 = "7d0a97d01d39cff9aa3e6db81f21fddb2435f4e6",
 )
 
 # Test-only dependencies below.
@@ -853,30 +860,30 @@
     sha1 = "42a25dc3219429f0e5d060061f71acb49bf010a0",
 )
 
-TRUTH_VERS = "0.43"
+TRUTH_VERS = "0.44"
 
 maven_jar(
     name = "truth",
     artifact = "com.google.truth:truth:" + TRUTH_VERS,
-    sha1 = "0cb9105957368f68d5fd771045bfa27d4a534836",
+    sha1 = "11eff954c0c14da7d43276d7b3bcf71463105368",
 )
 
 maven_jar(
     name = "truth-java8-extension",
     artifact = "com.google.truth.extensions:truth-java8-extension:" + TRUTH_VERS,
-    sha1 = "0707dd0b4eb2101aa85c4bb7caf52d9ae32f0a43",
+    sha1 = "2081a0721d3101e1cf559f013e59c6129b4b10b0",
 )
 
 maven_jar(
     name = "truth-liteproto-extension",
     artifact = "com.google.truth.extensions:truth-liteproto-extension:" + TRUTH_VERS,
-    sha1 = "67d833098345fc744c6fb2c38c739557b2d73742",
+    sha1 = "64f47e4e3f79b0a582573098b9c3c6b73599f7c6",
 )
 
 maven_jar(
     name = "truth-proto-extension",
     artifact = "com.google.truth.extensions:truth-proto-extension:" + TRUTH_VERS,
-    sha1 = "b9641436e7b48b3642ac7aa7b8f3d1b9d04bc44b",
+    sha1 = "c03fbc16087d8cb3bf0f3265a04566d4beb88a6d",
 )
 
 maven_jar(
@@ -1052,8 +1059,8 @@
 # and httpasyncclient as necessary.
 maven_jar(
     name = "elasticsearch-rest-client",
-    artifact = "org.elasticsearch.client:elasticsearch-rest-client:6.7.0",
-    sha1 = "032bf7044d1d5664ebdbfd6f2601027c806565ce",
+    artifact = "org.elasticsearch.client:elasticsearch-rest-client:7.0.0",
+    sha1 = "121d12f1c71f318be1a654e8a956e38d5b68e98a",
 )
 
 JACKSON_VERSION = "2.9.8"
@@ -1064,18 +1071,18 @@
     sha1 = "0f5a654e4675769c716e5b387830d19b501ca191",
 )
 
-TESTCONTAINERS_VERSION = "1.10.7"
+TESTCONTAINERS_VERSION = "1.11.2"
 
 maven_jar(
     name = "testcontainers",
     artifact = "org.testcontainers:testcontainers:" + TESTCONTAINERS_VERSION,
-    sha1 = "e7575fedfd010ca1ad80c8c9bf971a8057b1ff8a",
+    sha1 = "eae47ed24bb07270d4b60b5e2c3444c5bf3c8ea9",
 )
 
 maven_jar(
     name = "testcontainers-elasticsearch",
     artifact = "org.testcontainers:elasticsearch:" + TESTCONTAINERS_VERSION,
-    sha1 = "1ee43ebd81aea1f29bf60a56643bad80c134f998",
+    sha1 = "a327bd8cb68eb7146b36d754aee98a8018132d8f",
 )
 
 maven_jar(
@@ -1086,14 +1093,14 @@
 
 maven_jar(
     name = "visible-assertions",
-    artifact = "org.rnorth.visible-assertions:visible-assertions:2.1.0",
-    sha1 = "f2fcff2862860828ac38a5e1f14d941787c06b13",
+    artifact = "org.rnorth.visible-assertions:visible-assertions:2.1.2",
+    sha1 = "20d31a578030ec8e941888537267d3123c2ad1c1",
 )
 
 maven_jar(
     name = "jna",
-    artifact = "net.java.dev.jna:jna:4.5.1",
-    sha1 = "65bd0cacc9c79a21c6ed8e9f588577cd3c2f85b9",
+    artifact = "net.java.dev.jna:jna:5.2.0",
+    sha1 = "ed8b772eb077a9cb50e44e90899c66a9a6c00e67",
 )
 
 maven_jar(
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 5286ceb..9eaf781 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -132,7 +132,6 @@
 import com.google.gerrit.testing.FakeEmailSender.Message;
 import com.google.gerrit.testing.SshMode;
 import com.google.gson.Gson;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Module;
 import com.google.inject.Provider;
@@ -416,8 +415,8 @@
     user = accountCreator.user();
 
     // Evict and reindex accounts in case tests modify them.
-    evictAndReindexAccount(admin.getId());
-    evictAndReindexAccount(user.getId());
+    evictAndReindexAccount(admin.id());
+    evictAndReindexAccount(user.id());
 
     adminRestSession = new RestSession(server, admin);
     userRestSession = new RestSession(server, user);
@@ -553,7 +552,7 @@
   protected String registerRepoConnection(Project.NameKey p, TestAccount testAccount)
       throws Exception {
     InProcessProtocol.Context ctx =
-        new InProcessProtocol.Context(identifiedUserFactory, testAccount.getId(), p);
+        new InProcessProtocol.Context(identifiedUserFactory, testAccount.id(), p);
     Repository repo = repoManager.openRepository(p);
     toClose.add(repo);
     return inProcessProtocol.register(ctx, repo).toString();
@@ -605,7 +604,7 @@
   }
 
   protected PushOneCommit.Result createChange(String ref) throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result result = push.to(ref);
     result.assertOkStatus();
     return result;
@@ -621,7 +620,7 @@
     PushOneCommit.Result p1 =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 testRepo,
                 "parent 1",
                 ImmutableMap.of(file, "foo-1", "bar", "bar-1"))
@@ -633,7 +632,7 @@
     PushOneCommit.Result p2 =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 testRepo,
                 "parent 2",
                 ImmutableMap.of(file, "foo-2", "bar", "bar-2"))
@@ -641,7 +640,7 @@
 
     PushOneCommit m =
         pushFactory.create(
-            admin.getIdent(), testRepo, "merge", ImmutableMap.of(file, "foo-1", "bar", "bar-2"));
+            admin.newIdent(), testRepo, "merge", ImmutableMap.of(file, "foo-1", "bar", "bar-2"));
     m.setParents(ImmutableList.of(p1.getCommit(), p2.getCommit()));
     PushOneCommit.Result result = m.to(ref);
     result.assertOkStatus();
@@ -656,7 +655,7 @@
       String content)
       throws Exception {
     PushOneCommit.Result result =
-        pushFactory.create(admin.getIdent(), repo, commitMsg, fileName, content).to(ref);
+        pushFactory.create(admin.newIdent(), repo, commitMsg, fileName, content).to(ref);
     result.assertOkStatus();
     return result;
   }
@@ -675,7 +674,7 @@
 
   protected PushOneCommit.Result createChange(String subject, String fileName, String content)
       throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo, subject, fileName, content);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content);
     return push.to("refs/for/master");
   }
 
@@ -687,7 +686,7 @@
       String content,
       String topic)
       throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), repo, subject, fileName, content);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), repo, subject, fileName, content);
     return push.to("refs/for/" + branch + "%topic=" + name(topic));
   }
 
@@ -741,7 +740,7 @@
       String content)
       throws Exception {
     PushOneCommit push =
-        pushFactory.create(testAccount.getIdent(), repo, subject, fileName, content, changeId);
+        pushFactory.create(testAccount.newIdent(), repo, subject, fileName, content, changeId);
     return push.to(ref);
   }
 
@@ -767,7 +766,7 @@
   }
 
   private Context newRequestContext(TestAccount account) {
-    requestScopeOperations.setApiUser(account.getId());
+    requestScopeOperations.setApiUser(account.id());
     return atrScope.get();
   }
 
@@ -1061,7 +1060,7 @@
   }
 
   protected PushOneCommit.Result pushTo(String ref) throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     return push.to(ref);
   }
 
@@ -1087,12 +1086,12 @@
         .inOrder();
   }
 
-  protected PatchSet getPatchSet(PatchSet.Id psId) throws OrmException {
+  protected PatchSet getPatchSet(PatchSet.Id psId) {
     return changeDataFactory.create(project, psId.getParentKey()).patchSet(psId);
   }
 
   protected IdentifiedUser user(TestAccount testAccount) {
-    return identifiedUserFactory.create(testAccount.getId());
+    return identifiedUserFactory.create(testAccount.id());
   }
 
   protected RevisionResource parseCurrentRevisionResource(String changeId) throws Exception {
@@ -1356,7 +1355,7 @@
   }
 
   protected void assertNotifyTo(TestAccount expected) {
-    assertNotifyTo(expected.email, expected.fullName);
+    assertNotifyTo(expected.email(), expected.fullName());
   }
 
   protected void assertNotifyTo(String expectedEmail, String expectedFullname) {
@@ -1370,7 +1369,7 @@
   }
 
   protected void assertNotifyCc(TestAccount expected) {
-    assertNotifyCc(expected.emailAddress);
+    assertNotifyCc(expected.getEmailAddress());
   }
 
   protected void assertNotifyCc(String expectedEmail, String expectedFullname) {
@@ -1390,7 +1389,7 @@
   protected void assertNotifyBcc(TestAccount expected) {
     assertThat(sender.getMessages()).hasSize(1);
     Message m = sender.getMessages().get(0);
-    assertThat(m.rcpt()).containsExactly(expected.emailAddress);
+    assertThat(m.rcpt()).containsExactly(expected.getEmailAddress());
     assertThat(m.headers().get("To").isEmpty()).isTrue();
     assertThat(m.headers().get("Cc").isEmpty()).isTrue();
   }
@@ -1416,7 +1415,7 @@
   }
 
   protected void watch(PushOneCommit.Result r, ProjectWatchInfoConfiguration config)
-      throws OrmException, RestApiException {
+      throws RestApiException {
     watch(r.getChange().project().get(), config);
   }
 
@@ -1545,7 +1544,7 @@
     }
 
     public void save() throws Exception {
-      metaDataUpdate.setAuthor(identifiedUserFactory.create(admin.getId()));
+      metaDataUpdate.setAuthor(identifiedUserFactory.create(admin.id()));
       projectConfig.commit(metaDataUpdate);
       metaDataUpdate.close();
       metaDataUpdate = null;
diff --git a/java/com/google/gerrit/acceptance/AbstractNotificationTest.java b/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
index 1a6ed37..d6e4f42 100644
--- a/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractNotificationTest.java
@@ -60,7 +60,7 @@
 
   @Before
   public void enableReviewerByEmail() throws Exception {
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     ConfigInput conf = new ConfigInput();
     conf.enableReviewerByEmail = InheritableBoolean.TRUE;
     gApi.projects().name(project.get()).config(conf);
@@ -86,7 +86,7 @@
     if (record) {
       accountsModifyingEmailStrategy.add(account);
     }
-    requestScopeOperations.setApiUser(account.getId());
+    requestScopeOperations.setApiUser(account.id());
     GeneralPreferencesInfo prefs = gApi.accounts().self().getPreferences();
     prefs.emailStrategy = strategy;
     gApi.accounts().self().setPreferences(prefs);
@@ -214,7 +214,7 @@
 
     public FakeEmailSenderSubject noOneElse() {
       for (Map.Entry<NotifyType, TestAccount> watchEntry : users.watchers.entrySet()) {
-        if (!accountedFor.contains(watchEntry.getValue().email)) {
+        if (!accountedFor.contains(watchEntry.getValue().email())) {
           notTo(watchEntry.getKey());
         }
       }
@@ -267,7 +267,7 @@
     }
 
     private void rcpt(@Nullable RecipientType type, TestAccount account) {
-      rcpt(type, account.email);
+      rcpt(type, account.email());
     }
 
     public FakeEmailSenderSubject to(NotifyType... watches) {
@@ -336,7 +336,7 @@
     }
 
     private TestAccount evictAndCopy(TestAccount account) throws IOException {
-      evictAndReindexAccount(account.id);
+      evictAndReindexAccount(account.id());
       return account;
     }
 
@@ -365,7 +365,7 @@
         assignee = testAccount("assignee");
 
         watchingProjectOwner = testAccount("watchingProjectOwner", "Administrators");
-        requestScopeOperations.setApiUser(watchingProjectOwner.getId());
+        requestScopeOperations.setApiUser(watchingProjectOwner.id());
         watch(allProjects.get(), pwi -> pwi.notifyNewChanges = true);
 
         for (NotifyType watch : NotifyType.values()) {
@@ -373,7 +373,7 @@
             continue;
           }
           TestAccount watcher = testAccount(watch.toString());
-          requestScopeOperations.setApiUser(watcher.getId());
+          requestScopeOperations.setApiUser(watcher.id());
           watch(
               allProjects.get(),
               pwi -> {
@@ -404,20 +404,20 @@
     public TestAccount testAccount(String name) throws Exception {
       String username = name(name);
       TestAccount account = accountCreator.create(username, email(username), name);
-      accountsByEmail.put(account.email, account);
+      accountsByEmail.put(account.email(), account);
       return account;
     }
 
     public TestAccount testAccount(String name, String groupName) throws Exception {
       String username = name(name);
       TestAccount account = accountCreator.create(username, email(username), name, groupName);
-      accountsByEmail.put(account.email, account);
+      accountsByEmail.put(account.email(), account);
       return account;
     }
 
     String emailToName(String email) {
       if (accountsByEmail.containsKey(email)) {
-        return accountsByEmail.get(email).fullName;
+        return accountsByEmail.get(email).fullName();
       }
       return email;
     }
@@ -425,9 +425,9 @@
     protected void addReviewers(PushOneCommit.Result r) throws Exception {
       ReviewInput in =
           ReviewInput.noScore()
-              .reviewer(reviewer.email)
+              .reviewer(reviewer.email())
               .reviewer(reviewerByEmail)
-              .reviewer(ccer.email, ReviewerState.CC, false)
+              .reviewer(ccer.email(), ReviewerState.CC, false)
               .reviewer(ccerByEmail, ReviewerState.CC, false);
       ReviewResult result = gApi.changes().id(r.getChangeId()).revision("current").review(in);
       supportReviewersByEmail = true;
@@ -435,8 +435,8 @@
         supportReviewersByEmail = false;
         in =
             ReviewInput.noScore()
-                .reviewer(reviewer.email)
-                .reviewer(ccer.email, ReviewerState.CC, false);
+                .reviewer(reviewer.email())
+                .reviewer(ccer.email(), ReviewerState.CC, false);
         result = gApi.changes().id(r.getChangeId()).revision("current").review(in);
       }
       Truth.assertThat(result.reviewers.values().stream().allMatch(v -> v.error == null)).isTrue();
@@ -466,9 +466,9 @@
       if (pushOptions != null) {
         ref = ref + '%' + Joiner.on(',').join(pushOptions);
       }
-      requestScopeOperations.setApiUser(owner.getId());
+      requestScopeOperations.setApiUser(owner.id());
       repo = cloneProject(project, owner);
-      PushOneCommit push = pushFactory.create(owner.getIdent(), repo);
+      PushOneCommit push = pushFactory.create(owner.newIdent(), repo);
       result = push.to(ref);
       result.assertOkStatus();
       changeId = result.getChangeId();
@@ -488,10 +488,10 @@
     StagedChange(String ref) throws Exception {
       super(ref);
 
-      requestScopeOperations.setApiUser(starrer.getId());
+      requestScopeOperations.setApiUser(starrer.id());
       gApi.accounts().self().starChange(result.getChangeId());
 
-      requestScopeOperations.setApiUser(owner.getId());
+      requestScopeOperations.setApiUser(owner.id());
       addReviewers(result);
       sender.clear();
     }
@@ -511,7 +511,7 @@
 
   protected StagedChange stageReviewableWipChange() throws Exception {
     StagedChange sc = stageReviewableChange();
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     gApi.changes().id(sc.changeId).setWorkInProgress();
     sender.clear();
     return sc;
@@ -519,7 +519,7 @@
 
   protected StagedChange stageAbandonedReviewableChange() throws Exception {
     StagedChange sc = stageReviewableChange();
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     gApi.changes().id(sc.changeId).abandon();
     sender.clear();
     return sc;
@@ -527,7 +527,7 @@
 
   protected StagedChange stageAbandonedReviewableWipChange() throws Exception {
     StagedChange sc = stageReviewableWipChange();
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     gApi.changes().id(sc.changeId).abandon();
     sender.clear();
     return sc;
@@ -535,7 +535,7 @@
 
   protected StagedChange stageAbandonedWipChange() throws Exception {
     StagedChange sc = stageWipChange();
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     gApi.changes().id(sc.changeId).abandon();
     sender.clear();
     return sc;
diff --git a/java/com/google/gerrit/acceptance/AccountCreator.java b/java/com/google/gerrit/acceptance/AccountCreator.java
index d85613c..aeae2c2 100644
--- a/java/com/google/gerrit/acceptance/AccountCreator.java
+++ b/java/com/google/gerrit/acceptance/AccountCreator.java
@@ -20,7 +20,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.ServerInitiated;
@@ -31,7 +31,6 @@
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
 import com.google.gerrit.server.notedb.Sequences;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -108,7 +107,7 @@
       }
     }
 
-    account = new TestAccount(id, username, email, fullName, httpPass);
+    account = TestAccount.create(id, username, email, fullName, httpPass);
     if (username != null) {
       accounts.put(username, account);
     }
@@ -149,7 +148,7 @@
   }
 
   public void evict(Collection<Account.Id> ids) {
-    accounts.values().removeIf(a -> ids.contains(a.id));
+    accounts.values().removeIf(a -> ids.contains(a.id()));
   }
 
   public ImmutableList<TestAccount> getAll() {
@@ -157,7 +156,7 @@
   }
 
   private void addGroupMember(AccountGroup.UUID groupUuid, Account.Id accountId)
-      throws OrmException, IOException, NoSuchGroupException, ConfigInvalidException {
+      throws IOException, NoSuchGroupException, ConfigInvalidException {
     InternalGroupUpdate groupUpdate =
         InternalGroupUpdate.builder()
             .setMemberModification(memberIds -> Sets.union(memberIds, ImmutableSet.of(accountId)))
diff --git a/java/com/google/gerrit/acceptance/BUILD b/java/com/google/gerrit/acceptance/BUILD
index d61cf7a..ed0de9c 100644
--- a/java/com/google/gerrit/acceptance/BUILD
+++ b/java/com/google/gerrit/acceptance/BUILD
@@ -37,7 +37,6 @@
         "//lib:args4j",
         "//lib:gson",
         "//lib:guava-retrying",
-        "//lib:gwtorm",
         "//lib:h2",
         "//lib:jimfs",
         "//lib:jsch",
@@ -69,6 +68,7 @@
     testonly = True,
     srcs = glob(["**/*.java"]),
     exported_deps = [
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/gpg",
         "//java/com/google/gerrit/httpd/auth/openid",
         "//java/com/google/gerrit/index:query_exception",
@@ -119,7 +119,6 @@
         "//lib:args4j",
         "//lib:gson",
         "//lib:guava-retrying",
-        "//lib:gwtorm",
         "//lib:jsch",
         "//lib:servlet-api-3_1",
         "//lib/commons:lang",
diff --git a/java/com/google/gerrit/acceptance/DisabledChangeIndex.java b/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
index d39edec..a32c6d1 100644
--- a/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
+++ b/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.index.change.ChangeIndex;
 import com.google.gerrit.server.query.change.ChangeData;
-import java.io.IOException;
 import java.util.Optional;
 
 /**
@@ -53,17 +52,17 @@
   }
 
   @Override
-  public void replace(ChangeData obj) throws IOException {
+  public void replace(ChangeData obj) {
     throw new UnsupportedOperationException("ChangeIndex is disabled");
   }
 
   @Override
-  public void delete(Change.Id key) throws IOException {
+  public void delete(Change.Id key) {
     throw new UnsupportedOperationException("ChangeIndex is disabled");
   }
 
   @Override
-  public void deleteAll() throws IOException {
+  public void deleteAll() {
     throw new UnsupportedOperationException("ChangeIndex is disabled");
   }
 
@@ -74,12 +73,12 @@
   }
 
   @Override
-  public void markReady(boolean ready) throws IOException {
+  public void markReady(boolean ready) {
     throw new UnsupportedOperationException("ChangeIndex is disabled");
   }
 
   @Override
-  public Optional<ChangeData> get(Change.Id key, QueryOptions opts) throws IOException {
+  public Optional<ChangeData> get(Change.Id key, QueryOptions opts) {
     throw new UnsupportedOperationException("ChangeIndex is disabled");
   }
 }
diff --git a/java/com/google/gerrit/acceptance/EventRecorder.java b/java/com/google/gerrit/acceptance/EventRecorder.java
index 1af71b8..6c6e1bf 100644
--- a/java/com/google/gerrit/acceptance/EventRecorder.java
+++ b/java/com/google/gerrit/acceptance/EventRecorder.java
@@ -55,7 +55,7 @@
     }
 
     public EventRecorder create(TestAccount user) {
-      return new EventRecorder(eventListeners, userFactory.create(user.id));
+      return new EventRecorder(eventListeners, userFactory.create(user.id()));
     }
   }
 
diff --git a/java/com/google/gerrit/acceptance/HttpSession.java b/java/com/google/gerrit/acceptance/HttpSession.java
index fdd1fce..833c53b 100644
--- a/java/com/google/gerrit/acceptance/HttpSession.java
+++ b/java/com/google/gerrit/acceptance/HttpSession.java
@@ -37,7 +37,7 @@
     this.account = account;
     if (account != null) {
       executor.auth(
-          new HttpHost(uri.getHost(), uri.getPort()), account.username, account.httpPassword);
+          new HttpHost(uri.getHost(), uri.getPort()), account.username(), account.httpPassword());
     }
   }
 
diff --git a/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java b/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
index 844d6c8..a704d2f 100644
--- a/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
+++ b/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
@@ -17,6 +17,7 @@
 import static com.google.inject.Scopes.SINGLETON;
 
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.events.LifecycleListener;
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.metrics.DisabledMetricMaker;
@@ -30,8 +31,6 @@
 import com.google.gerrit.server.schema.SchemaCreator;
 import com.google.gerrit.server.schema.SchemaModule;
 import com.google.gerrit.testing.InMemoryRepositoryManager;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
 import com.google.inject.Inject;
 import com.google.inject.ProvisionException;
 import java.io.IOException;
@@ -91,8 +90,8 @@
     public void start() {
       try {
         schemaCreator.ensureCreated();
-      } catch (OrmException | IOException | ConfigInvalidException e) {
-        throw new OrmRuntimeException(e);
+      } catch (IOException | ConfigInvalidException e) {
+        throw new StorageException(e);
       }
     }
 
diff --git a/java/com/google/gerrit/acceptance/PushOneCommit.java b/java/com/google/gerrit/acceptance/PushOneCommit.java
index f16f1d3..e15dd40 100644
--- a/java/com/google/gerrit/acceptance/PushOneCommit.java
+++ b/java/com/google/gerrit/acceptance/PushOneCommit.java
@@ -32,7 +32,6 @@
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
@@ -334,15 +333,15 @@
       this.resSubj = subject;
     }
 
-    public ChangeData getChange() throws OrmException {
+    public ChangeData getChange() {
       return Iterables.getOnlyElement(queryProvider.get().byKeyPrefix(changeId));
     }
 
-    public PatchSet getPatchSet() throws OrmException {
+    public PatchSet getPatchSet() {
       return getChange().currentPatchSet();
     }
 
-    public PatchSet.Id getPatchSetId() throws OrmException {
+    public PatchSet.Id getPatchSetId() {
       return getChange().change().currentPatchSetId();
     }
 
@@ -359,8 +358,7 @@
     }
 
     public void assertChange(
-        Change.Status expectedStatus, String expectedTopic, TestAccount... expectedReviewers)
-        throws OrmException {
+        Change.Status expectedStatus, String expectedTopic, TestAccount... expectedReviewers) {
       assertChange(
           expectedStatus, expectedTopic, Arrays.asList(expectedReviewers), ImmutableList.of());
     }
@@ -369,8 +367,7 @@
         Change.Status expectedStatus,
         String expectedTopic,
         List<TestAccount> expectedReviewers,
-        List<TestAccount> expectedCcs)
-        throws OrmException {
+        List<TestAccount> expectedCcs) {
       Change c = getChange().change();
       assertThat(c.getSubject()).isEqualTo(resSubj);
       assertThat(c.getStatus()).isEqualTo(expectedStatus);
@@ -380,8 +377,7 @@
     }
 
     private void assertReviewers(
-        Change c, ReviewerStateInternal state, List<TestAccount> expectedReviewers)
-        throws OrmException {
+        Change c, ReviewerStateInternal state, List<TestAccount> expectedReviewers) {
       Iterable<Account.Id> actualIds =
           approvalsUtil.getReviewers(notesFactory.createChecked(c)).byState(state);
       assertThat(actualIds)
diff --git a/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java b/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java
index d4fd318..19910db 100644
--- a/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java
+++ b/java/com/google/gerrit/acceptance/ReadOnlyChangeIndex.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.index.change.ChangeIndex;
 import com.google.gerrit.server.query.change.ChangeData;
-import java.io.IOException;
 
 class ReadOnlyChangeIndex implements ChangeIndex {
   private final ChangeIndex index;
@@ -46,17 +45,17 @@
   }
 
   @Override
-  public void replace(ChangeData obj) throws IOException {
+  public void replace(ChangeData obj) {
     // do nothing
   }
 
   @Override
-  public void delete(Change.Id key) throws IOException {
+  public void delete(Change.Id key) {
     // do nothing
   }
 
   @Override
-  public void deleteAll() throws IOException {
+  public void deleteAll() {
     // do nothing
   }
 
@@ -67,7 +66,7 @@
   }
 
   @Override
-  public void markReady(boolean ready) throws IOException {
+  public void markReady(boolean ready) {
     // do nothing
   }
 }
diff --git a/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java b/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
index 84e798c..bd8a926 100644
--- a/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
+++ b/java/com/google/gerrit/acceptance/ReindexGroupsAtStartup.java
@@ -63,15 +63,7 @@
       throw new IllegalStateException("Unable to reindex groups, tests may fail", e);
     }
 
-    allGroupReferences.forEach(
-        group -> {
-          try {
-            groupIndexer.index(group.getUUID());
-          } catch (IOException e) {
-            throw new IllegalStateException(
-                String.format("Unable to index %s, tests may fail", group), e);
-          }
-        });
+    allGroupReferences.forEach(group -> groupIndexer.index(group.getUUID()));
   }
 
   @Override
diff --git a/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java b/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java
index f585f81..2f0ffcb 100644
--- a/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java
+++ b/java/com/google/gerrit/acceptance/ReindexProjectsAtStartup.java
@@ -20,7 +20,6 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.inject.Inject;
 import com.google.inject.Scopes;
-import java.io.IOException;
 
 /** Reindex all projects at Gerrit daemon startup. */
 public class ReindexProjectsAtStartup implements LifecycleListener {
@@ -42,16 +41,7 @@
 
   @Override
   public void start() {
-    repoMgr.list().stream()
-        .forEach(
-            projectName -> {
-              try {
-                projectIndexer.index(projectName);
-              } catch (IOException e) {
-                throw new IllegalStateException(
-                    String.format("Unable to index %s, tests may fail", projectName), e);
-              }
-            });
+    repoMgr.list().stream().forEach(projectIndexer::index);
   }
 
   @Override
diff --git a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
index db93009..29d0b35 100644
--- a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
+++ b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
@@ -59,7 +59,7 @@
       this.server = server;
       Injector i = server.getTestInjector();
       if (adminId == null) {
-        adminId = i.getInstance(AccountCreator.class).admin().getId();
+        adminId = i.getInstance(AccountCreator.class).admin().id();
       }
       ctx = i.getInstance(OneOffRequestContext.class).openAs(adminId);
       GerritApi gApi = i.getInstance(GerritApi.class);
diff --git a/java/com/google/gerrit/acceptance/TestAccount.java b/java/com/google/gerrit/acceptance/TestAccount.java
index ac197bd..c937aed 100644
--- a/java/com/google/gerrit/acceptance/TestAccount.java
+++ b/java/com/google/gerrit/acceptance/TestAccount.java
@@ -14,67 +14,80 @@
 
 package com.google.gerrit.acceptance;
 
-import static java.util.stream.Collectors.toList;
+import static com.google.common.collect.ImmutableList.toImmutableList;
 
-import com.google.common.base.MoreObjects;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Streams;
 import com.google.common.net.InetAddresses;
+import com.google.gerrit.common.Nullable;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.reviewdb.client.Account;
 import java.net.InetSocketAddress;
 import java.util.Arrays;
-import java.util.List;
 import org.apache.http.client.utils.URIBuilder;
 import org.eclipse.jgit.lib.PersonIdent;
 
-public class TestAccount {
-  public static List<Account.Id> ids(List<TestAccount> accounts) {
-    return accounts.stream().map(a -> a.id).collect(toList());
+@AutoValue
+public abstract class TestAccount {
+  public static ImmutableList<Account.Id> ids(Iterable<TestAccount> accounts) {
+    return Streams.stream(accounts).map(TestAccount::id).collect(toImmutableList());
   }
 
-  public static List<String> names(List<TestAccount> accounts) {
-    return accounts.stream().map(a -> a.fullName).collect(toList());
+  public static ImmutableList<String> names(Iterable<TestAccount> accounts) {
+    return Streams.stream(accounts).map(TestAccount::fullName).collect(toImmutableList());
   }
 
-  public static List<String> names(TestAccount... accounts) {
+  public static ImmutableList<String> names(TestAccount... accounts) {
     return names(Arrays.asList(accounts));
   }
 
-  public final Account.Id id;
-  public final String username;
-  public final String email;
-  public final Address emailAddress;
-  public final String fullName;
-  public final String httpPassword;
-
-  TestAccount(Account.Id id, String username, String email, String fullName, String httpPassword) {
-    this.id = id;
-    this.username = username;
-    this.email = email;
-    this.emailAddress = new Address(fullName, email);
-    this.fullName = fullName;
-    this.httpPassword = httpPassword;
+  static TestAccount create(
+      Account.Id id,
+      @Nullable String username,
+      @Nullable String email,
+      @Nullable String fullName,
+      @Nullable String httpPassword) {
+    return new AutoValue_TestAccount(id, username, email, fullName, httpPassword);
   }
 
-  public PersonIdent getIdent() {
-    return new PersonIdent(fullName, email);
+  public abstract Account.Id id();
+
+  @Nullable
+  public abstract String username();
+
+  @Nullable
+  public abstract String email();
+
+  @Nullable
+  public abstract String fullName();
+
+  @Nullable
+  public abstract String httpPassword();
+
+  public PersonIdent newIdent() {
+    return new PersonIdent(fullName(), email());
   }
 
   public String getHttpUrl(GerritServer server) {
     InetSocketAddress addr = server.getHttpAddress();
     return new URIBuilder()
         .setScheme("http")
-        .setUserInfo(username, httpPassword)
+        .setUserInfo(username(), httpPassword())
         .setHost(InetAddresses.toUriString(addr.getAddress()))
         .setPort(addr.getPort())
         .toString();
   }
 
-  public Account.Id getId() {
-    return id;
-  }
-
-  @Override
-  public String toString() {
-    return MoreObjects.toStringHelper(this).add("id", id).add("username", username).toString();
+  public Address getEmailAddress() {
+    // Address is weird enough that it's safer and clearer to create a new instance in a
+    // non-abstract method rather than, say, having an abstract emailAddress() as part of this
+    // AutoValue class. Specifically:
+    //  * Email is not specified as @Nullable in Address, but it is nullable in this class. If this
+    //    is a problem, at least it's a problem only for users of TestAccount that actually call
+    //    emailAddress().
+    //  * Address#equals only considers email, not name, whereas TestAccount#equals should include
+    //    name.
+    return new Address(fullName(), email());
   }
 }
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
index 113c19c..7641e47 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/AccountOperationsImpl.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.server.account.InternalAccountUpdate;
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.notedb.Sequences;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.Optional;
@@ -68,7 +67,7 @@
   }
 
   private AccountState createAccount(AccountsUpdate.AccountUpdater accountUpdater)
-      throws OrmException, IOException, ConfigInvalidException {
+      throws IOException, ConfigInvalidException {
     Account.Id accountId = new Account.Id(seq.nextAccountId());
     return accountsUpdate.insert("Create Test Account", accountId, accountUpdater);
   }
@@ -146,7 +145,7 @@
     }
 
     private void updateAccount(TestAccountUpdate accountUpdate)
-        throws OrmException, IOException, ConfigInvalidException {
+        throws IOException, ConfigInvalidException {
       AccountsUpdate.AccountUpdater accountUpdater =
           (account, updateBuilder) -> fillBuilder(updateBuilder, accountUpdate, accountId);
       Optional<AccountState> updatedAccount = updateAccount(accountUpdater);
@@ -154,7 +153,7 @@
     }
 
     private Optional<AccountState> updateAccount(AccountsUpdate.AccountUpdater accountUpdater)
-        throws OrmException, IOException, ConfigInvalidException {
+        throws IOException, ConfigInvalidException {
       return accountsUpdate.update("Update Test Account", accountId, accountUpdater);
     }
 
diff --git a/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java b/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
index 0cb5cf3..4847fdb 100644
--- a/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
+++ b/java/com/google/gerrit/acceptance/testsuite/account/TestSshKeys.java
@@ -55,14 +55,14 @@
   public KeyPair getKeyPair(com.google.gerrit.acceptance.TestAccount account) throws Exception {
     checkState(sshEnabled, "Requested SSH key pair, but SSH is disabled");
     checkState(
-        account.username != null,
+        account.username() != null,
         "Requested SSH key pair for account %s, but username is not set",
-        account.id);
+        account.id());
 
-    String username = account.username;
+    String username = account.username();
     KeyPair keyPair = sshKeyPairs.get(username);
     if (keyPair == null) {
-      keyPair = createKeyPair(account.id, username, account.email);
+      keyPair = createKeyPair(account.id(), username, account.email());
       sshKeyPairs.put(username, keyPair);
     }
     return keyPair;
diff --git a/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java
index ac818c9..e0ddee5 100644
--- a/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImpl.java
@@ -16,7 +16,8 @@
 
 import static com.google.common.base.Preconditions.checkState;
 
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.ServerInitiated;
@@ -27,8 +28,6 @@
 import com.google.gerrit.server.group.db.InternalGroupCreation;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
 import com.google.gerrit.server.notedb.Sequences;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.Optional;
@@ -70,7 +69,7 @@
   }
 
   private AccountGroup.UUID createNewGroup(TestGroupCreation groupCreation)
-      throws ConfigInvalidException, IOException, OrmException {
+      throws ConfigInvalidException, IOException {
     InternalGroupCreation internalGroupCreation = toInternalGroupCreation(groupCreation);
     InternalGroupUpdate internalGroupUpdate = toInternalGroupUpdate(groupCreation);
     InternalGroup internalGroup =
@@ -78,8 +77,7 @@
     return internalGroup.getGroupUUID();
   }
 
-  private InternalGroupCreation toInternalGroupCreation(TestGroupCreation groupCreation)
-      throws OrmException {
+  private InternalGroupCreation toInternalGroupCreation(TestGroupCreation groupCreation) {
     AccountGroup.Id groupId = new AccountGroup.Id(seq.nextGroupId());
     String groupName = groupCreation.name().orElse("group-with-id-" + groupId.get());
     AccountGroup.UUID groupUuid = GroupUUID.make(groupName, serverIdent);
@@ -148,7 +146,7 @@
     }
 
     private void updateGroup(TestGroupUpdate groupUpdate)
-        throws OrmDuplicateKeyException, NoSuchGroupException, ConfigInvalidException, IOException {
+        throws DuplicateKeyException, NoSuchGroupException, ConfigInvalidException, IOException {
       InternalGroupUpdate internalGroupUpdate = toInternalGroupUpdate(groupUpdate);
       groupsUpdate.updateGroup(groupUuid, internalGroupUpdate);
     }
diff --git a/java/com/google/gerrit/common/BUILD b/java/com/google/gerrit/common/BUILD
index 29f3f72..a434c34 100644
--- a/java/com/google/gerrit/common/BUILD
+++ b/java/com/google/gerrit/common/BUILD
@@ -22,9 +22,9 @@
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/prettify:server",
         "//java/com/google/gerrit/reviewdb:server",
+        "//java/com/google/gwtorm",
         "//java/org/eclipse/jgit:server",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:servlet-api-3_1",
         "//lib/auto:auto-value",
         "//lib/auto:auto-value-annotations",
diff --git a/java/com/google/gerrit/common/data/AccessSection.java b/java/com/google/gerrit/common/data/AccessSection.java
index b3da199..3670e96 100644
--- a/java/com/google/gerrit/common/data/AccessSection.java
+++ b/java/com/google/gerrit/common/data/AccessSection.java
@@ -25,16 +25,34 @@
 import java.util.Set;
 
 /** Portion of a {@link Project} describing access rules. */
-public class AccessSection extends RefConfigSection implements Comparable<AccessSection> {
+public final class AccessSection implements Comparable<AccessSection> {
   /** Special name given to the global capabilities; not a valid reference. */
   public static final String GLOBAL_CAPABILITIES = "GLOBAL_CAPABILITIES";
+  /** Pattern that matches all references in a project. */
+  public static final String ALL = "refs/*";
 
-  protected List<Permission> permissions;
+  /** Pattern that matches all branches in a project. */
+  public static final String HEADS = "refs/heads/*";
 
-  protected AccessSection() {}
+  /** Prefix that triggers a regular expression pattern. */
+  public static final String REGEX_PREFIX = "^";
 
-  public AccessSection(String refPattern) {
-    super(refPattern);
+  /** Name of the access section. It could be a ref pattern or something else. */
+  private String name;
+
+  private List<Permission> permissions;
+
+  public AccessSection(String name) {
+    this.name = name;
+  }
+
+  /** @return true if the name is likely to be a valid reference section name. */
+  public static boolean isValidRefSectionName(String name) {
+    return name.startsWith("refs/") || name.startsWith("^refs/");
+  }
+
+  public String getName() {
+    return name;
   }
 
   public ImmutableList<Permission> getPermissions() {
@@ -145,7 +163,12 @@
 
   @Override
   public boolean equals(Object obj) {
-    if (!super.equals(obj) || !(obj instanceof AccessSection)) {
+    if (!(obj instanceof AccessSection)) {
+      return false;
+    }
+
+    AccessSection other = (AccessSection) obj;
+    if (!getName().equals(other.getName())) {
       return false;
     }
     return new HashSet<>(getPermissions())
@@ -160,6 +183,7 @@
         hashCode += permission.hashCode();
       }
     }
+    hashCode += getName().hashCode();
     return hashCode;
   }
 }
diff --git a/java/com/google/gerrit/common/data/RefConfigSection.java b/java/com/google/gerrit/common/data/RefConfigSection.java
deleted file mode 100644
index 663379a..0000000
--- a/java/com/google/gerrit/common/data/RefConfigSection.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (C) 2011 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.common.data;
-
-public abstract class RefConfigSection {
-  /** Pattern that matches all references in a project. */
-  public static final String ALL = "refs/*";
-
-  /** Pattern that matches all branches in a project. */
-  public static final String HEADS = "refs/heads/*";
-
-  /** Prefix that triggers a regular expression pattern. */
-  public static final String REGEX_PREFIX = "^";
-
-  /** @return true if the name is likely to be a valid reference section name. */
-  public static boolean isValid(String name) {
-    return name.startsWith("refs/") || name.startsWith("^refs/");
-  }
-
-  protected String name;
-
-  public RefConfigSection() {}
-
-  public RefConfigSection(String name) {
-    setName(name);
-  }
-
-  public String getName() {
-    return name;
-  }
-
-  public void setName(String name) {
-    this.name = name;
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    if (!(obj instanceof RefConfigSection)) {
-      return false;
-    }
-    return name.equals(((RefConfigSection) obj).name);
-  }
-
-  @Override
-  public int hashCode() {
-    return name.hashCode();
-  }
-}
diff --git a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
index 9669c375..86f1083 100644
--- a/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
+++ b/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
@@ -32,6 +32,7 @@
 import com.google.gerrit.elasticsearch.builders.QueryBuilder;
 import com.google.gerrit.elasticsearch.builders.SearchSourceBuilder;
 import com.google.gerrit.elasticsearch.bulk.DeleteRequest;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.FieldType;
 import com.google.gerrit.index.Index;
@@ -53,7 +54,6 @@
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
-import com.google.gwtorm.server.OrmException;
 import com.google.protobuf.MessageLite;
 import java.io.IOException;
 import java.io.InputStream;
@@ -167,23 +167,23 @@
   }
 
   @Override
-  public void markReady(boolean ready) throws IOException {
+  public void markReady(boolean ready) {
     IndexUtils.setReady(sitePaths, indexNameRaw, schema.getVersion(), ready);
   }
 
   @Override
-  public void delete(K id) throws IOException {
+  public void delete(K id) {
     String uri = getURI(type, BULK);
     Response response = postRequest(uri, getDeleteActions(id), getRefreshParam());
     int statusCode = response.getStatusLine().getStatusCode();
     if (statusCode != HttpStatus.SC_OK) {
-      throw new IOException(
+      throw new StorageException(
           String.format("Failed to delete %s from index %s: %s", id, indexName, statusCode));
     }
   }
 
   @Override
-  public void deleteAll() throws IOException {
+  public void deleteAll() {
     // Delete the index, if it exists.
     String endpoint = indexName + client.adapter().indicesExistParam();
     Response response = performRequest("HEAD", endpoint);
@@ -192,7 +192,7 @@
       response = performRequest("DELETE", indexName);
       statusCode = response.getStatusLine().getStatusCode();
       if (statusCode != HttpStatus.SC_OK) {
-        throw new IOException(
+        throw new StorageException(
             String.format("Failed to delete index %s: %s", indexName, statusCode));
       }
     }
@@ -205,7 +205,7 @@
     statusCode = response.getStatusLine().getStatusCode();
     if (statusCode != HttpStatus.SC_OK) {
       String error = String.format("Failed to create index %s: %s", indexName, statusCode);
-      throw new IOException(error);
+      throw new StorageException(error);
     }
   }
 
@@ -307,21 +307,24 @@
     return sortArray;
   }
 
-  protected String getURI(String type, String request) throws UnsupportedEncodingException {
-    String encodedIndexName = URLEncoder.encode(indexName, UTF_8.toString());
-    if (SEARCH.equals(request) && client.adapter().omitTypeFromSearch()) {
-      return encodedIndexName + "/" + request;
+  protected String getURI(String type, String request) {
+    try {
+      String encodedIndexName = URLEncoder.encode(indexName, UTF_8.toString());
+      if (SEARCH.equals(request) && client.adapter().omitTypeFromSearch()) {
+        return encodedIndexName + "/" + request;
+      }
+      String encodedType = URLEncoder.encode(type, UTF_8.toString());
+      return encodedIndexName + "/" + encodedType + "/" + request;
+    } catch (UnsupportedEncodingException e) {
+      throw new StorageException(e);
     }
-    String encodedType = URLEncoder.encode(type, UTF_8.toString());
-    return encodedIndexName + "/" + encodedType + "/" + request;
   }
 
-  protected Response postRequest(String uri, Object payload) throws IOException {
+  protected Response postRequest(String uri, Object payload) {
     return performRequest("POST", uri, payload);
   }
 
-  protected Response postRequest(String uri, Object payload, Map<String, String> params)
-      throws IOException {
+  protected Response postRequest(String uri, Object payload, Map<String, String> params) {
     return performRequest("POST", uri, payload, params);
   }
 
@@ -329,18 +332,16 @@
     return target.substring(0, target.length() - 1) + "," + addition.substring(1);
   }
 
-  private Response performRequest(String method, String uri) throws IOException {
+  private Response performRequest(String method, String uri) {
     return performRequest(method, uri, null);
   }
 
-  private Response performRequest(String method, String uri, @Nullable Object payload)
-      throws IOException {
+  private Response performRequest(String method, String uri, @Nullable Object payload) {
     return performRequest(method, uri, payload, Collections.emptyMap());
   }
 
   private Response performRequest(
-      String method, String uri, @Nullable Object payload, Map<String, String> params)
-      throws IOException {
+      String method, String uri, @Nullable Object payload, Map<String, String> params) {
     Request request = new Request(method, uri.startsWith("/") ? uri : "/" + uri);
     if (payload != null) {
       String payloadStr = payload instanceof String ? (String) payload : payload.toString();
@@ -349,7 +350,11 @@
     for (Map.Entry<String, String> entry : params.entrySet()) {
       request.addParameter(entry.getKey(), entry.getValue());
     }
-    return client.get().performRequest(request);
+    try {
+      return client.get().performRequest(request);
+    } catch (IOException e) {
+      throw new StorageException(e);
+    }
   }
 
   protected class ElasticQuerySource implements DataSource<V> {
@@ -377,16 +382,16 @@
     }
 
     @Override
-    public ResultSet<V> read() throws OrmException {
+    public ResultSet<V> read() {
       return readImpl((doc) -> AbstractElasticIndex.this.fromDocument(doc, opts.fields()));
     }
 
     @Override
-    public ResultSet<FieldBundle> readRaw() throws OrmException {
+    public ResultSet<FieldBundle> readRaw() {
       return readImpl(AbstractElasticIndex.this::toFieldBundle);
     }
 
-    private <T> ResultSet<T> readImpl(Function<JsonObject, T> mapper) throws OrmException {
+    private <T> ResultSet<T> readImpl(Function<JsonObject, T> mapper) {
       try {
         String uri = getURI(index, SEARCH);
         Response response =
@@ -412,7 +417,7 @@
         }
         return new ListResultSet<>(ImmutableList.of());
       } catch (IOException e) {
-        throw new OrmException(e);
+        throw new StorageException(e);
       }
     }
   }
diff --git a/java/com/google/gerrit/elasticsearch/BUILD b/java/com/google/gerrit/elasticsearch/BUILD
index d5c586b..f919aad 100644
--- a/java/com/google/gerrit/elasticsearch/BUILD
+++ b/java/com/google/gerrit/elasticsearch/BUILD
@@ -4,6 +4,7 @@
     visibility = ["//visibility:public"],
     deps = [
         "//java/com/google/gerrit/common:annotations",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/index",
         "//java/com/google/gerrit/index:query_exception",
@@ -14,7 +15,6 @@
         "//java/com/google/gerrit/server",
         "//lib:gson",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:protobuf",
         "//lib/commons:codec",
         "//lib/commons:lang",
diff --git a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
index d0b70ae..c25aa90 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticAccountIndex.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.elasticsearch.bulk.BulkRequest;
 import com.google.gerrit.elasticsearch.bulk.IndexRequest;
 import com.google.gerrit.elasticsearch.bulk.UpdateRequest;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.query.DataSource;
@@ -38,7 +39,6 @@
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
 import java.util.Set;
 import org.apache.http.HttpStatus;
 import org.elasticsearch.client.Response;
@@ -73,7 +73,7 @@
   }
 
   @Override
-  public void replace(AccountState as) throws IOException {
+  public void replace(AccountState as) {
     BulkRequest bulk =
         new IndexRequest(getId(as), indexName, type, client.adapter())
             .add(new UpdateRequest<>(schema, as));
@@ -82,7 +82,7 @@
     Response response = postRequest(uri, bulk, getRefreshParam());
     int statusCode = response.getStatusLine().getStatusCode();
     if (statusCode != HttpStatus.SC_OK) {
-      throw new IOException(
+      throw new StorageException(
           String.format(
               "Failed to replace account %s in index %s: %s",
               as.getAccount().getId(), indexName, statusCode));
diff --git a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
index 344e10c..5782399 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
@@ -33,6 +33,7 @@
 import com.google.gerrit.elasticsearch.bulk.DeleteRequest;
 import com.google.gerrit.elasticsearch.bulk.IndexRequest;
 import com.google.gerrit.elasticsearch.bulk.UpdateRequest;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.query.DataSource;
@@ -57,10 +58,8 @@
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
@@ -107,20 +106,16 @@
   }
 
   @Override
-  public void replace(ChangeData cd) throws IOException {
+  public void replace(ChangeData cd) {
     String deleteIndex;
     String insertIndex;
 
-    try {
-      if (cd.change().isNew()) {
-        insertIndex = OPEN_CHANGES;
-        deleteIndex = CLOSED_CHANGES;
-      } else {
-        insertIndex = CLOSED_CHANGES;
-        deleteIndex = OPEN_CHANGES;
-      }
-    } catch (OrmException e) {
-      throw new IOException(e);
+    if (cd.change().isNew()) {
+      insertIndex = OPEN_CHANGES;
+      deleteIndex = CLOSED_CHANGES;
+    } else {
+      insertIndex = CLOSED_CHANGES;
+      deleteIndex = OPEN_CHANGES;
     }
 
     ElasticQueryAdapter adapter = client.adapter();
@@ -135,7 +130,7 @@
     Response response = postRequest(uri, bulk, getRefreshParam());
     int statusCode = response.getStatusLine().getStatusCode();
     if (statusCode != HttpStatus.SC_OK) {
-      throw new IOException(
+      throw new StorageException(
           String.format(
               "Failed to replace change %s in index %s: %s", cd.getId(), indexName, statusCode));
     }
diff --git a/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java b/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
index e74f208..ecda1ee 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticGroupIndex.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.elasticsearch.bulk.BulkRequest;
 import com.google.gerrit.elasticsearch.bulk.IndexRequest;
 import com.google.gerrit.elasticsearch.bulk.UpdateRequest;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.query.DataSource;
@@ -36,7 +37,6 @@
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
 import java.util.Set;
 import org.apache.http.HttpStatus;
 import org.elasticsearch.client.Response;
@@ -71,7 +71,7 @@
   }
 
   @Override
-  public void replace(InternalGroup group) throws IOException {
+  public void replace(InternalGroup group) {
     BulkRequest bulk =
         new IndexRequest(getId(group), indexName, type, client.adapter())
             .add(new UpdateRequest<>(schema, group));
@@ -80,7 +80,7 @@
     Response response = postRequest(uri, bulk, getRefreshParam());
     int statusCode = response.getStatusLine().getStatusCode();
     if (statusCode != HttpStatus.SC_OK) {
-      throw new IOException(
+      throw new StorageException(
           String.format(
               "Failed to replace group %s in index %s: %s",
               group.getGroupUUID().get(), indexName, statusCode));
diff --git a/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java b/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java
index 8510559..daf3702 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticProjectIndex.java
@@ -18,6 +18,7 @@
 import com.google.gerrit.elasticsearch.bulk.BulkRequest;
 import com.google.gerrit.elasticsearch.bulk.IndexRequest;
 import com.google.gerrit.elasticsearch.bulk.UpdateRequest;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.project.ProjectData;
@@ -36,7 +37,6 @@
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
 import java.util.Set;
 import org.apache.http.HttpStatus;
 import org.elasticsearch.client.Response;
@@ -71,7 +71,7 @@
   }
 
   @Override
-  public void replace(ProjectData projectState) throws IOException {
+  public void replace(ProjectData projectState) {
     BulkRequest bulk =
         new IndexRequest(projectState.getProject().getName(), indexName, type, client.adapter())
             .add(new UpdateRequest<>(schema, projectState));
@@ -80,7 +80,7 @@
     Response response = postRequest(uri, bulk, getRefreshParam());
     int statusCode = response.getStatusLine().getStatusCode();
     if (statusCode != HttpStatus.SC_OK) {
-      throw new IOException(
+      throw new StorageException(
           String.format(
               "Failed to replace project %s in index %s: %s",
               projectState.getProject().getName(), indexName, statusCode));
diff --git a/java/com/google/gerrit/exceptions/BUILD b/java/com/google/gerrit/exceptions/BUILD
new file mode 100644
index 0000000..50bf883
--- /dev/null
+++ b/java/com/google/gerrit/exceptions/BUILD
@@ -0,0 +1,6 @@
+java_library(
+    name = "exceptions",
+    srcs = glob(["*.java"]),
+    visibility = ["//visibility:public"],
+    deps = ["//java/com/google/gerrit/reviewdb:server"],
+)
diff --git a/java/com/google/gerrit/common/errors/EmailException.java b/java/com/google/gerrit/exceptions/DuplicateKeyException.java
similarity index 63%
copy from java/com/google/gerrit/common/errors/EmailException.java
copy to java/com/google/gerrit/exceptions/DuplicateKeyException.java
index 635335d..d052450 100644
--- a/java/com/google/gerrit/common/errors/EmailException.java
+++ b/java/com/google/gerrit/exceptions/DuplicateKeyException.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 The Android Open Source Project
+// Copyright 2009 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,18 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.common.errors;
+package com.google.gerrit.exceptions;
 
-public class EmailException extends Exception {
+/** Indicates one or more entities were concurrently inserted with the same key. */
+public class DuplicateKeyException extends StorageException {
   private static final long serialVersionUID = 1L;
 
-  public static final String MESSAGE = "Mail Error: ";
-
-  public EmailException(String msg) {
-    super(MESSAGE + msg);
+  public DuplicateKeyException(String msg) {
+    super(msg);
   }
 
-  public EmailException(String msg, Throwable why) {
-    super(MESSAGE + msg, why);
+  public DuplicateKeyException(String msg, Throwable why) {
+    super(msg, why);
   }
 }
diff --git a/java/com/google/gerrit/common/errors/EmailException.java b/java/com/google/gerrit/exceptions/EmailException.java
similarity index 95%
rename from java/com/google/gerrit/common/errors/EmailException.java
rename to java/com/google/gerrit/exceptions/EmailException.java
index 635335d..a278eed 100644
--- a/java/com/google/gerrit/common/errors/EmailException.java
+++ b/java/com/google/gerrit/exceptions/EmailException.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.common.errors;
+package com.google.gerrit.exceptions;
 
 public class EmailException extends Exception {
   private static final long serialVersionUID = 1L;
diff --git a/java/com/google/gerrit/common/errors/InvalidNameException.java b/java/com/google/gerrit/exceptions/InvalidNameException.java
similarity index 95%
rename from java/com/google/gerrit/common/errors/InvalidNameException.java
rename to java/com/google/gerrit/exceptions/InvalidNameException.java
index d975aef..4539500 100644
--- a/java/com/google/gerrit/common/errors/InvalidNameException.java
+++ b/java/com/google/gerrit/exceptions/InvalidNameException.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.common.errors;
+package com.google.gerrit.exceptions;
 
 /** Error indicating the entity name is invalid as supplied. */
 public class InvalidNameException extends Exception {
diff --git a/java/com/google/gerrit/common/errors/InvalidSshKeyException.java b/java/com/google/gerrit/exceptions/InvalidSshKeyException.java
similarity index 95%
rename from java/com/google/gerrit/common/errors/InvalidSshKeyException.java
rename to java/com/google/gerrit/exceptions/InvalidSshKeyException.java
index 3398417..8baba20 100644
--- a/java/com/google/gerrit/common/errors/InvalidSshKeyException.java
+++ b/java/com/google/gerrit/exceptions/InvalidSshKeyException.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.common.errors;
+package com.google.gerrit.exceptions;
 
 /** Error indicating the SSH key string is invalid as supplied. */
 public class InvalidSshKeyException extends Exception {
diff --git a/java/com/google/gerrit/common/errors/NameAlreadyUsedException.java b/java/com/google/gerrit/exceptions/NameAlreadyUsedException.java
similarity index 95%
rename from java/com/google/gerrit/common/errors/NameAlreadyUsedException.java
rename to java/com/google/gerrit/exceptions/NameAlreadyUsedException.java
index ea20e2e..df2631b 100644
--- a/java/com/google/gerrit/common/errors/NameAlreadyUsedException.java
+++ b/java/com/google/gerrit/exceptions/NameAlreadyUsedException.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.common.errors;
+package com.google.gerrit.exceptions;
 
 /** Error indicating entity name is already taken by another entity. */
 public class NameAlreadyUsedException extends Exception {
diff --git a/java/com/google/gerrit/common/errors/NoSuchAccountException.java b/java/com/google/gerrit/exceptions/NoSuchAccountException.java
similarity index 95%
rename from java/com/google/gerrit/common/errors/NoSuchAccountException.java
rename to java/com/google/gerrit/exceptions/NoSuchAccountException.java
index 90bf624..d753128 100644
--- a/java/com/google/gerrit/common/errors/NoSuchAccountException.java
+++ b/java/com/google/gerrit/exceptions/NoSuchAccountException.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.common.errors;
+package com.google.gerrit.exceptions;
 
 /** Error indicating the account requested doesn't exist. */
 public class NoSuchAccountException extends Exception {
diff --git a/java/com/google/gerrit/common/errors/NoSuchEntityException.java b/java/com/google/gerrit/exceptions/NoSuchEntityException.java
similarity index 95%
rename from java/com/google/gerrit/common/errors/NoSuchEntityException.java
rename to java/com/google/gerrit/exceptions/NoSuchEntityException.java
index 1829c8b..c812a38 100644
--- a/java/com/google/gerrit/common/errors/NoSuchEntityException.java
+++ b/java/com/google/gerrit/exceptions/NoSuchEntityException.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.common.errors;
+package com.google.gerrit.exceptions;
 
 /** Error indicating the entity requested doesn't exist. */
 public class NoSuchEntityException extends Exception {
diff --git a/java/com/google/gerrit/common/errors/NoSuchGroupException.java b/java/com/google/gerrit/exceptions/NoSuchGroupException.java
similarity index 97%
rename from java/com/google/gerrit/common/errors/NoSuchGroupException.java
rename to java/com/google/gerrit/exceptions/NoSuchGroupException.java
index 6e3db9e..dca28cb 100644
--- a/java/com/google/gerrit/common/errors/NoSuchGroupException.java
+++ b/java/com/google/gerrit/exceptions/NoSuchGroupException.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.common.errors;
+package com.google.gerrit.exceptions;
 
 import com.google.gerrit.reviewdb.client.AccountGroup;
 
diff --git a/java/com/google/gerrit/common/errors/NotSignedInException.java b/java/com/google/gerrit/exceptions/NotSignedInException.java
similarity index 95%
rename from java/com/google/gerrit/common/errors/NotSignedInException.java
rename to java/com/google/gerrit/exceptions/NotSignedInException.java
index 65caf02..1919dc39 100644
--- a/java/com/google/gerrit/common/errors/NotSignedInException.java
+++ b/java/com/google/gerrit/exceptions/NotSignedInException.java
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.gerrit.common.errors;
+package com.google.gerrit.exceptions;
 
 /** Error stating the user must be signed-in in order to perform this action. */
 public class NotSignedInException extends Exception {
diff --git a/java/com/google/gerrit/exceptions/StorageException.java b/java/com/google/gerrit/exceptions/StorageException.java
new file mode 100644
index 0000000..a788fff
--- /dev/null
+++ b/java/com/google/gerrit/exceptions/StorageException.java
@@ -0,0 +1,44 @@
+// Copyright 2008 Google Inc.
+//
+// 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.exceptions;
+
+/**
+ * Any read/write error in a storage layer.
+ *
+ * <p>This includes but is not limited to:
+ *
+ * <ul>
+ *   <li>NoteDb exceptions
+ *   <li>Secondary index exceptions
+ *   <li>{@code AccountPatchReviewStore} exceptions
+ *   <li>Wrapped JGit exceptions
+ *   <li>Other wrapped {@code IOException}s
+ * </ul>
+ */
+public class StorageException extends RuntimeException {
+  private static final long serialVersionUID = 1L;
+
+  public StorageException(String message) {
+    super(message);
+  }
+
+  public StorageException(String message, Throwable why) {
+    super(message, why);
+  }
+
+  public StorageException(Throwable why) {
+    super(why);
+  }
+}
diff --git a/java/com/google/gerrit/git/testing/PushResultSubject.java b/java/com/google/gerrit/git/testing/PushResultSubject.java
index 724ca17..f5c9810 100644
--- a/java/com/google/gerrit/git/testing/PushResultSubject.java
+++ b/java/com/google/gerrit/git/testing/PushResultSubject.java
@@ -41,7 +41,7 @@
   }
 
   public void hasNoMessages() {
-    check()
+    check("hasNoMessages()")
         .withMessage("expected no messages")
         .that(Strings.nullToEmpty(trimMessages()))
         .isEqualTo("");
@@ -57,7 +57,7 @@
     checkArgument(expectedLines.length > 0, "use hasNoMessages()");
     isNotNull();
     Iterable<String> got = Splitter.on("\n").split(trimMessages());
-    check("messages()").that(got).containsAllIn(expectedLines).inOrder();
+    check("messages()").that(got).containsAtLeastElementsIn(expectedLines).inOrder();
   }
 
   private String trimMessages() {
diff --git a/java/com/google/gerrit/gpg/BUILD b/java/com/google/gerrit/gpg/BUILD
index 0aa6ca2..fb93b03 100644
--- a/java/com/google/gerrit/gpg/BUILD
+++ b/java/com/google/gerrit/gpg/BUILD
@@ -4,11 +4,11 @@
     visibility = ["//visibility:public"],
     deps = [
         "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/reviewdb:server",
         "//java/com/google/gerrit/server",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib/bouncycastle:bcpg-neverlink",
         "//lib/bouncycastle:bcprov-neverlink",
         "//lib/flogger:api",
diff --git a/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java b/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
index b6ecbc5..9c08857 100644
--- a/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
+++ b/java/com/google/gerrit/gpg/GerritPublicKeyChecker.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.UrlFormatter;
 import com.google.gerrit.server.query.account.InternalAccountQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -135,7 +134,7 @@
         return checkIdsForExpectedUser(key);
       }
       return checkIdsForArbitraryUser(key);
-    } catch (PGPException | OrmException e) {
+    } catch (PGPException | RuntimeException e) {
       String msg = "Error checking user IDs for key";
       logger.atWarning().withCause(e).log("%s %s", msg, keyIdToString(key.getKeyID()));
       return CheckResult.bad(msg);
@@ -156,7 +155,7 @@
     return CheckResult.bad(missingUserIds(allowedUserIds));
   }
 
-  private CheckResult checkIdsForArbitraryUser(PGPPublicKey key) throws PGPException, OrmException {
+  private CheckResult checkIdsForArbitraryUser(PGPPublicKey key) throws PGPException {
     List<AccountState> accountStates = accountQueryProvider.get().byExternalId(toExtIdKey(key));
     if (accountStates.isEmpty()) {
       return CheckResult.bad("Key is not associated with any users");
diff --git a/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java b/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
index 967259a..b7b03db 100644
--- a/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
+++ b/java/com/google/gerrit/gpg/api/GpgApiAdapterImpl.java
@@ -28,7 +28,6 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.GpgApiAdapter;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -67,7 +66,7 @@
       throws RestApiException, GpgException {
     try {
       return gpgKeys.get().list().apply(account);
-    } catch (OrmException | PGPException | IOException e) {
+    } catch (PGPException | IOException e) {
       throw new GpgException(e);
     }
   }
@@ -81,7 +80,7 @@
     in.delete = delete;
     try {
       return postGpgKeys.get().apply(account, in);
-    } catch (PGPException | OrmException | IOException | ConfigInvalidException e) {
+    } catch (PGPException | IOException | ConfigInvalidException e) {
       throw new GpgException(e);
     }
   }
@@ -91,7 +90,7 @@
       throws RestApiException, GpgException {
     try {
       return gpgKeyApiFactory.create(gpgKeys.get().parse(account, idStr));
-    } catch (PGPException | OrmException | IOException e) {
+    } catch (PGPException | IOException e) {
       throw new GpgException(e);
     }
   }
diff --git a/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java b/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
index 25b472d..cf09acf 100644
--- a/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
+++ b/java/com/google/gerrit/gpg/api/GpgKeyApiImpl.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.gpg.server.DeleteGpgKey;
 import com.google.gerrit.gpg.server.GpgKey;
 import com.google.gerrit.gpg.server.GpgKeys;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
@@ -57,7 +56,7 @@
   public void delete() throws RestApiException {
     try {
       delete.apply(rsrc, new Input());
-    } catch (PGPException | OrmException | IOException | ConfigInvalidException e) {
+    } catch (PGPException | IOException | ConfigInvalidException e) {
       throw new RestApiException("Cannot delete GPG key", e);
     }
   }
diff --git a/java/com/google/gerrit/gpg/server/DeleteGpgKey.java b/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
index a636a8b..69e106c 100644
--- a/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
+++ b/java/com/google/gerrit/gpg/server/DeleteGpgKey.java
@@ -30,7 +30,6 @@
 import com.google.gerrit.server.account.AccountsUpdate;
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.account.externalids.ExternalIds;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -63,7 +62,7 @@
 
   @Override
   public Response<?> apply(GpgKey rsrc, Input input)
-      throws RestApiException, PGPException, OrmException, IOException, ConfigInvalidException {
+      throws RestApiException, PGPException, IOException, ConfigInvalidException {
     PGPPublicKey key = rsrc.getKeyRing().getPublicKey();
     String fingerprint = BaseEncoding.base16().encode(key.getFingerprint());
     Optional<ExternalId> extId = externalIds.get(ExternalId.Key.create(SCHEME_GPGKEY, fingerprint));
diff --git a/java/com/google/gerrit/gpg/server/GpgKeys.java b/java/com/google/gerrit/gpg/server/GpgKeys.java
index e555f07..16592f8 100644
--- a/java/com/google/gerrit/gpg/server/GpgKeys.java
+++ b/java/com/google/gerrit/gpg/server/GpgKeys.java
@@ -39,7 +39,6 @@
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.account.externalids.ExternalIds;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -86,7 +85,7 @@
 
   @Override
   public GpgKey parse(AccountResource parent, IdString id)
-      throws ResourceNotFoundException, PGPException, OrmException, IOException {
+      throws ResourceNotFoundException, PGPException, IOException {
     checkVisible(self, parent);
 
     ExternalId gpgKeyExtId = findGpgKey(id.get(), getGpgExtIds(parent));
@@ -142,7 +141,7 @@
   public class ListGpgKeys implements RestReadView<AccountResource> {
     @Override
     public Map<String, GpgKeyInfo> apply(AccountResource rsrc)
-        throws OrmException, PGPException, IOException, ResourceNotFoundException {
+        throws PGPException, IOException, ResourceNotFoundException {
       checkVisible(self, rsrc);
       Map<String, GpgKeyInfo> keys = new HashMap<>();
       try (PublicKeyStore store = storeProvider.get()) {
diff --git a/java/com/google/gerrit/gpg/server/PostGpgKeys.java b/java/com/google/gerrit/gpg/server/PostGpgKeys.java
index 7d08fca..57c95a9 100644
--- a/java/com/google/gerrit/gpg/server/PostGpgKeys.java
+++ b/java/com/google/gerrit/gpg/server/PostGpgKeys.java
@@ -26,7 +26,7 @@
 import com.google.common.collect.Maps;
 import com.google.common.flogger.FluentLogger;
 import com.google.common.io.BaseEncoding;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.accounts.GpgKeysInput;
 import com.google.gerrit.extensions.common.GpgKeyInfo;
 import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -50,7 +50,6 @@
 import com.google.gerrit.server.account.externalids.ExternalIds;
 import com.google.gerrit.server.mail.send.AddKeySender;
 import com.google.gerrit.server.query.account.InternalAccountQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -108,7 +107,7 @@
   @Override
   public Map<String, GpgKeyInfo> apply(AccountResource rsrc, GpgKeysInput input)
       throws ResourceNotFoundException, BadRequestException, ResourceConflictException,
-          PGPException, OrmException, IOException, ConfigInvalidException {
+          PGPException, IOException, ConfigInvalidException {
     GpgKeys.checkVisible(self, rsrc);
 
     Collection<ExternalId> existingExtIds =
@@ -249,7 +248,7 @@
     return ExternalId.Key.create(SCHEME_GPGKEY, BaseEncoding.base16().encode(fp));
   }
 
-  private Account getAccountByExternalId(ExternalId.Key extIdKey) throws OrmException {
+  private Account getAccountByExternalId(ExternalId.Key extIdKey) {
     List<AccountState> accountStates = accountQueryProvider.get().byExternalId(extIdKey);
 
     if (accountStates.isEmpty()) {
diff --git a/java/com/google/gerrit/httpd/BUILD b/java/com/google/gerrit/httpd/BUILD
index def4b30..5ed8169 100644
--- a/java/com/google/gerrit/httpd/BUILD
+++ b/java/com/google/gerrit/httpd/BUILD
@@ -10,6 +10,7 @@
     deps = [
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/git",
         "//java/com/google/gerrit/json",
@@ -30,7 +31,6 @@
         "//lib:args4j",
         "//lib:gson",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:jsch",
         "//lib:servlet-api-3_1",
         "//lib:soy",
diff --git a/java/com/google/gerrit/httpd/NumericChangeIdRedirectServlet.java b/java/com/google/gerrit/httpd/NumericChangeIdRedirectServlet.java
index 164f957..d53a5c5 100644
--- a/java/com/google/gerrit/httpd/NumericChangeIdRedirectServlet.java
+++ b/java/com/google/gerrit/httpd/NumericChangeIdRedirectServlet.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.restapi.change.ChangesCollection;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -61,7 +60,7 @@
     } catch (ResourceConflictException | ResourceNotFoundException e) {
       rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
       return;
-    } catch (OrmException | PermissionBackendException e) {
+    } catch (PermissionBackendException | RuntimeException e) {
       throw new IOException("Unable to lookup change " + id.id, e);
     }
     String path =
diff --git a/java/com/google/gerrit/httpd/RunAsFilter.java b/java/com/google/gerrit/httpd/RunAsFilter.java
index 1ff8580..15dbcab 100644
--- a/java/com/google/gerrit/httpd/RunAsFilter.java
+++ b/java/com/google/gerrit/httpd/RunAsFilter.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.google.inject.servlet.ServletModule;
@@ -110,7 +109,7 @@
       } catch (UnprocessableEntityException e) {
         replyError(req, res, SC_FORBIDDEN, "no account matches " + RUN_AS, null);
         return;
-      } catch (OrmException | IOException | ConfigInvalidException e) {
+      } catch (IOException | ConfigInvalidException | RuntimeException e) {
         logger.atWarning().withCause(e).log("cannot resolve account for %s", RUN_AS);
         replyError(req, res, SC_INTERNAL_SERVER_ERROR, "cannot resolve " + RUN_AS, e);
         return;
diff --git a/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java b/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
index d6ff2ec..552e667 100644
--- a/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
+++ b/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.query.account.InternalAccountQuery;
 import com.google.gerrit.util.http.CacheHeaders;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -188,33 +187,20 @@
   }
 
   private AuthResult byUserName(String userName) {
-    try {
-      List<AccountState> accountStates =
-          queryProvider.get().byExternalId(SCHEME_USERNAME, userName);
-      if (accountStates.isEmpty()) {
-        getServletContext().log("No accounts with username " + userName + " found");
-        return null;
-      }
-      if (accountStates.size() > 1) {
-        getServletContext().log("Multiple accounts with username " + userName + " found");
-        return null;
-      }
-      return auth(accountStates.get(0).getAccount().getId());
-    } catch (OrmException e) {
-      getServletContext().log("cannot query account index", e);
+    List<AccountState> accountStates = queryProvider.get().byExternalId(SCHEME_USERNAME, userName);
+    if (accountStates.isEmpty()) {
+      getServletContext().log("No accounts with username " + userName + " found");
       return null;
     }
+    if (accountStates.size() > 1) {
+      getServletContext().log("Multiple accounts with username " + userName + " found");
+      return null;
+    }
+    return auth(accountStates.get(0).getAccount().getId());
   }
 
   private Optional<AuthResult> byPreferredEmail(String email) {
-    try {
-      Optional<AccountState> match =
-          queryProvider.get().byPreferredEmail(email).stream().findFirst();
-      return auth(match);
-    } catch (OrmException e) {
-      getServletContext().log("cannot query database", e);
-      return Optional.empty();
-    }
+    return auth(queryProvider.get().byPreferredEmail(email).stream().findFirst());
   }
 
   private Optional<AuthResult> byAccountId(String idStr) {
diff --git a/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java b/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java
index fd2f628..1b7e477 100644
--- a/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java
+++ b/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java
@@ -31,7 +31,6 @@
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.config.AuthConfig;
 import com.google.gerrit.util.http.CacheHeaders;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -128,7 +127,7 @@
         logger.atFine().log(
             "Associating external identity \"%s\" to user \"%s\"", remoteExternalId, user);
         updateRemoteExternalId(arsp, remoteExternalId);
-      } catch (AccountException | OrmException | ConfigInvalidException e) {
+      } catch (AccountException | ConfigInvalidException e) {
         logger.atSevere().withCause(e).log(
             "Unable to associate external identity \"%s\" to user \"%s\"", remoteExternalId, user);
         rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
@@ -152,7 +151,7 @@
   }
 
   private void updateRemoteExternalId(AuthResult arsp, String remoteAuthToken)
-      throws AccountException, OrmException, IOException, ConfigInvalidException {
+      throws AccountException, IOException, ConfigInvalidException {
     accountManager.updateLink(
         arsp.getAccountId(),
         new AuthRequest(ExternalId.Key.create(SCHEME_EXTERNAL, remoteAuthToken)));
diff --git a/java/com/google/gerrit/httpd/auth/oauth/BUILD b/java/com/google/gerrit/httpd/auth/oauth/BUILD
index 7315ce1..dd3e5fc 100644
--- a/java/com/google/gerrit/httpd/auth/oauth/BUILD
+++ b/java/com/google/gerrit/httpd/auth/oauth/BUILD
@@ -6,6 +6,7 @@
     visibility = ["//visibility:public"],
     deps = [
         "//java/com/google/gerrit/common:annotations",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/httpd",
         "//java/com/google/gerrit/reviewdb:server",
@@ -13,7 +14,6 @@
         "//java/com/google/gerrit/server/audit",
         "//lib:gson",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:servlet-api-3_1",
         "//lib/commons:codec",
         "//lib/flogger:api",
diff --git a/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java b/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
index b780fa0..0c8a1a10 100644
--- a/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
+++ b/java/com/google/gerrit/httpd/auth/oauth/OAuthSession.java
@@ -35,7 +35,6 @@
 import com.google.gerrit.server.account.AuthResult;
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.auth.oauth.OAuthTokenCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.servlet.SessionScoped;
@@ -188,7 +187,7 @@
       logger.atInfo().log("OAuth2: linking claimed identity to %s", claimedId.get().toString());
       try {
         accountManager.link(claimedId.get(), req);
-      } catch (OrmException | ConfigInvalidException e) {
+      } catch (ConfigInvalidException e) {
         logger.atSevere().log(
             "Cannot link: %s to user identity:\n  Claimed ID: %s is %s",
             user.getExternalId(), claimedId.get(), claimedIdentifier);
@@ -203,7 +202,7 @@
       throws AccountException, IOException {
     try {
       accountManager.link(identifiedUser.get().getAccountId(), areq);
-    } catch (OrmException | ConfigInvalidException e) {
+    } catch (ConfigInvalidException e) {
       logger.atSevere().log(
           "Cannot link: %s to user identity: %s",
           user.getExternalId(), identifiedUser.get().getAccountId());
diff --git a/java/com/google/gerrit/httpd/auth/openid/BUILD b/java/com/google/gerrit/httpd/auth/openid/BUILD
index f80e9d5..f09f93d 100644
--- a/java/com/google/gerrit/httpd/auth/openid/BUILD
+++ b/java/com/google/gerrit/httpd/auth/openid/BUILD
@@ -8,6 +8,7 @@
         # We want all these deps to be provided_deps
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/httpd",
         "//java/com/google/gerrit/reviewdb:server",
@@ -15,7 +16,7 @@
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/server/audit",
         "//lib:guava",
-        "//lib:gwtorm",
+        "//java/com/google/gwtorm",
         "//lib:servlet-api-3_1",
         "//lib/commons:codec",
         "//lib/flogger:api",
diff --git a/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java b/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
index a51a0ab..08f2d52 100644
--- a/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
+++ b/java/com/google/gerrit/httpd/auth/openid/OAuthSessionOverOpenID.java
@@ -33,7 +33,6 @@
 import com.google.gerrit.server.account.AccountManager;
 import com.google.gerrit.server.account.AuthResult;
 import com.google.gerrit.server.account.externalids.ExternalId;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.servlet.SessionScoped;
@@ -164,7 +163,7 @@
           logger.atFine().log("Claimed account already exists: link to it.");
           try {
             accountManager.link(claimedId.get(), areq);
-          } catch (OrmException | ConfigInvalidException e) {
+          } catch (ConfigInvalidException e) {
             logger.atSevere().log(
                 "Cannot link: %s to user identity:\n  Claimed ID: %s is %s",
                 user.getExternalId(), claimedId.get(), claimedIdentifier);
@@ -178,7 +177,7 @@
         try {
           logger.atFine().log("Linking \"%s\" to \"%s\"", user.getExternalId(), accountId);
           accountManager.link(accountId, areq);
-        } catch (OrmException | ConfigInvalidException e) {
+        } catch (ConfigInvalidException e) {
           logger.atSevere().log(
               "Cannot link: %s to user identity: %s", user.getExternalId(), accountId);
           rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
diff --git a/java/com/google/gerrit/httpd/init/BUILD b/java/com/google/gerrit/httpd/init/BUILD
index d557c0e..df072b2 100644
--- a/java/com/google/gerrit/httpd/init/BUILD
+++ b/java/com/google/gerrit/httpd/init/BUILD
@@ -26,7 +26,6 @@
         "//java/com/google/gerrit/server/schema",
         "//java/com/google/gerrit/sshd",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:servlet-api-3_1",
         "//lib/flogger:api",
         "//lib/guice",
diff --git a/java/com/google/gerrit/httpd/raw/CatServlet.java b/java/com/google/gerrit/httpd/raw/CatServlet.java
index 1e60d31..1d0e7d8b 100644
--- a/java/com/google/gerrit/httpd/raw/CatServlet.java
+++ b/java/com/google/gerrit/httpd/raw/CatServlet.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -145,7 +144,7 @@
     } catch (ResourceConflictException | NoSuchChangeException | AuthException e) {
       rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
       return;
-    } catch (OrmException | PermissionBackendException | IOException e) {
+    } catch (PermissionBackendException | IOException e) {
       getServletContext().log("Cannot query database", e);
       rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
       return;
diff --git a/java/com/google/gerrit/index/BUILD b/java/com/google/gerrit/index/BUILD
index 9d61c02..dfdc014 100644
--- a/java/com/google/gerrit/index/BUILD
+++ b/java/com/google/gerrit/index/BUILD
@@ -20,13 +20,13 @@
         ":query_exception",
         "//antlr3:query_parser",
         "//java/com/google/gerrit/common:annotations",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/json",
         "//java/com/google/gerrit/metrics",
         "//java/com/google/gerrit/reviewdb:server",
         "//java/com/google/gerrit/server/logging",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib/antlr:java-runtime",
         "//lib/auto:auto-value",
         "//lib/auto:auto-value-annotations",
diff --git a/java/com/google/gerrit/index/FieldDef.java b/java/com/google/gerrit/index/FieldDef.java
index f565e52..fb48104 100644
--- a/java/com/google/gerrit/index/FieldDef.java
+++ b/java/com/google/gerrit/index/FieldDef.java
@@ -19,7 +19,7 @@
 
 import com.google.common.base.CharMatcher;
 import com.google.gerrit.common.Nullable;
-import com.google.gwtorm.server.OrmException;
+import com.google.gerrit.exceptions.StorageException;
 import java.io.IOException;
 import java.sql.Timestamp;
 
@@ -62,7 +62,7 @@
   @FunctionalInterface
   public interface Getter<I, T> {
     @Nullable
-    T get(I input) throws OrmException, IOException;
+    T get(I input) throws IOException;
   }
 
   public static class Builder<T> {
@@ -133,14 +133,13 @@
    *
    * @param input input object.
    * @return the field value(s) to index.
-   * @throws OrmException
    */
   @Nullable
-  public T get(I input) throws OrmException {
+  public T get(I input) {
     try {
       return getter.get(input);
     } catch (IOException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
   }
 
diff --git a/java/com/google/gerrit/index/Index.java b/java/com/google/gerrit/index/Index.java
index f60c08f..44f8b42 100644
--- a/java/com/google/gerrit/index/Index.java
+++ b/java/com/google/gerrit/index/Index.java
@@ -15,13 +15,12 @@
 package com.google.gerrit.index;
 
 import com.google.common.collect.ImmutableList;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.query.DataSource;
 import com.google.gerrit.index.query.FieldBundle;
 import com.google.gerrit.index.query.IndexPredicate;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
 import java.util.Optional;
 
 /**
@@ -48,24 +47,18 @@
    * searchers, but should be visible within a reasonable amount of time.
    *
    * @param obj document object
-   * @throws IOException
    */
-  void replace(V obj) throws IOException;
+  void replace(V obj);
 
   /**
    * Delete a document from the index by key.
    *
    * @param key document key
-   * @throws IOException
    */
-  void delete(K key) throws IOException;
+  void delete(K key);
 
-  /**
-   * Delete all documents from the index.
-   *
-   * @throws IOException
-   */
-  void deleteAll() throws IOException;
+  /** Delete all documents from the index. */
+  void deleteAll();
 
   /**
    * Convert the given operator predicate into a source searching the index and returning only the
@@ -91,20 +84,17 @@
    * @param opts query options. Options that do not make sense in the context of a single document,
    *     such as start, will be ignored.
    * @return a single document if present.
-   * @throws IOException
    */
-  default Optional<V> get(K key, QueryOptions opts) throws IOException {
+  default Optional<V> get(K key, QueryOptions opts) {
     opts = opts.withStart(0).withLimit(2);
     ImmutableList<V> results;
     try {
       results = getSource(keyPredicate(key), opts).read().toList();
     } catch (QueryParseException e) {
-      throw new IOException("Unexpected QueryParseException during get()", e);
-    } catch (OrmException e) {
-      throw new IOException(e);
+      throw new StorageException("Unexpected QueryParseException during get()", e);
     }
     if (results.size() > 1) {
-      throw new IOException("Multiple results found in index for key " + key + ": " + results);
+      throw new StorageException("Multiple results found in index for key " + key + ": " + results);
     }
     return results.stream().findFirst();
   }
@@ -116,20 +106,17 @@
    * @param opts query options. Options that do not make sense in the context of a single document,
    *     such as start, will be ignored.
    * @return an abstraction of a raw index document to retrieve fields from.
-   * @throws IOException
    */
-  default Optional<FieldBundle> getRaw(K key, QueryOptions opts) throws IOException {
+  default Optional<FieldBundle> getRaw(K key, QueryOptions opts) {
     opts = opts.withStart(0).withLimit(2);
     ImmutableList<FieldBundle> results;
     try {
       results = getSource(keyPredicate(key), opts).readRaw().toList();
     } catch (QueryParseException e) {
-      throw new IOException("Unexpected QueryParseException during get()", e);
-    } catch (OrmException e) {
-      throw new IOException(e);
+      throw new StorageException("Unexpected QueryParseException during get()", e);
     }
     if (results.size() > 1) {
-      throw new IOException("Multiple results found in index for key " + key + ": " + results);
+      throw new StorageException("Multiple results found in index for key " + key + ": " + results);
     }
     return results.stream().findFirst();
   }
@@ -146,7 +133,6 @@
    * Mark whether this index is up-to-date and ready to serve reads.
    *
    * @param ready whether the index is ready
-   * @throws IOException
    */
-  void markReady(boolean ready) throws IOException;
+  void markReady(boolean ready);
 }
diff --git a/java/com/google/gerrit/index/Schema.java b/java/com/google/gerrit/index/Schema.java
index 2b3c63e..e633bfa 100644
--- a/java/com/google/gerrit/index/Schema.java
+++ b/java/com/google/gerrit/index/Schema.java
@@ -22,7 +22,6 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.flogger.FluentLogger;
-import com.google.gwtorm.server.OrmException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -179,7 +178,7 @@
               Object v;
               try {
                 v = f.get(obj);
-              } catch (OrmException e) {
+              } catch (RuntimeException e) {
                 logger.atSevere().withCause(e).log(
                     "error getting field %s of %s", f.getName(), obj);
                 return null;
diff --git a/java/com/google/gerrit/index/project/ProjectIndexer.java b/java/com/google/gerrit/index/project/ProjectIndexer.java
index 44dccfe..1ca29f5 100644
--- a/java/com/google/gerrit/index/project/ProjectIndexer.java
+++ b/java/com/google/gerrit/index/project/ProjectIndexer.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.index.project;
 
 import com.google.gerrit.reviewdb.client.Project;
-import java.io.IOException;
 
 public interface ProjectIndexer {
 
@@ -24,5 +23,5 @@
    *
    * @param nameKey name key of project to index.
    */
-  void index(Project.NameKey nameKey) throws IOException;
+  void index(Project.NameKey nameKey);
 }
diff --git a/java/com/google/gerrit/index/query/AndPredicate.java b/java/com/google/gerrit/index/query/AndPredicate.java
index 7fba05f..ae13fb3 100644
--- a/java/com/google/gerrit/index/query/AndPredicate.java
+++ b/java/com/google/gerrit/index/query/AndPredicate.java
@@ -16,7 +16,6 @@
 
 import static com.google.common.base.Preconditions.checkState;
 
-import com.google.gwtorm.server.OrmException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -82,7 +81,7 @@
   }
 
   @Override
-  public boolean match(T object) throws OrmException {
+  public boolean match(T object) {
     for (Predicate<T> c : children) {
       checkState(
           c.isMatchable(),
diff --git a/java/com/google/gerrit/index/query/AndSource.java b/java/com/google/gerrit/index/query/AndSource.java
index 649dc32..7d817d2 100644
--- a/java/com/google/gerrit/index/query/AndSource.java
+++ b/java/com/google/gerrit/index/query/AndSource.java
@@ -17,12 +17,10 @@
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.collect.ImmutableList.toImmutableList;
 
-import com.google.common.base.Throwables;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
+import com.google.gerrit.exceptions.StorageException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
@@ -76,26 +74,9 @@
   }
 
   @Override
-  public ResultSet<T> read() throws OrmException {
-    try {
-      return readImpl();
-    } catch (OrmRuntimeException err) {
-      if (err.getCause() != null) {
-        Throwables.throwIfInstanceOf(err.getCause(), OrmException.class);
-      }
-      throw new OrmException(err);
-    }
-  }
-
-  @Override
-  public ResultSet<FieldBundle> readRaw() throws OrmException {
-    // TOOD(hiesel): Implement
-    throw new UnsupportedOperationException("not implemented");
-  }
-
-  private ResultSet<T> readImpl() throws OrmException {
+  public ResultSet<T> read() {
     if (source == null) {
-      throw new OrmException("No DataSource: " + this);
+      throw new StorageException("No DataSource: " + this);
     }
     List<T> r = new ArrayList<>();
     T last = null;
@@ -142,12 +123,18 @@
   }
 
   @Override
+  public ResultSet<FieldBundle> readRaw() {
+    // TOOD(hiesel): Implement
+    throw new UnsupportedOperationException("not implemented");
+  }
+
+  @Override
   public boolean isMatchable() {
     return isVisibleToPredicate != null || super.isMatchable();
   }
 
   @Override
-  public boolean match(T object) throws OrmException {
+  public boolean match(T object) {
     if (isVisibleToPredicate != null && !isVisibleToPredicate.match(object)) {
       return false;
     }
@@ -164,7 +151,7 @@
         .transformAndConcat(this::transformBuffer);
   }
 
-  protected List<T> transformBuffer(List<T> buffer) throws OrmRuntimeException {
+  protected List<T> transformBuffer(List<T> buffer) {
     return buffer;
   }
 
diff --git a/java/com/google/gerrit/index/query/DataSource.java b/java/com/google/gerrit/index/query/DataSource.java
index a82337f..2c2ba53 100644
--- a/java/com/google/gerrit/index/query/DataSource.java
+++ b/java/com/google/gerrit/index/query/DataSource.java
@@ -14,15 +14,13 @@
 
 package com.google.gerrit.index.query;
 
-import com.google.gwtorm.server.OrmException;
-
 public interface DataSource<T> {
   /** @return an estimate of the number of results from {@link #read()}. */
   int getCardinality();
 
   /** @return read from the database and return the results. */
-  ResultSet<T> read() throws OrmException;
+  ResultSet<T> read();
 
   /** @return read from the database and return the raw results. */
-  ResultSet<FieldBundle> readRaw() throws OrmException;
+  ResultSet<FieldBundle> readRaw();
 }
diff --git a/java/com/google/gerrit/index/query/IndexedQuery.java b/java/com/google/gerrit/index/query/IndexedQuery.java
index 8df46a7..d9e33ea 100644
--- a/java/com/google/gerrit/index/query/IndexedQuery.java
+++ b/java/com/google/gerrit/index/query/IndexedQuery.java
@@ -16,9 +16,9 @@
 
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableList;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.Index;
 import com.google.gerrit.index.QueryOptions;
-import com.google.gwtorm.server.OrmException;
 import java.util.Collection;
 import java.util.List;
 
@@ -77,17 +77,17 @@
   }
 
   @Override
-  public ResultSet<T> read() throws OrmException {
+  public ResultSet<T> read() {
     return source.read();
   }
 
   @Override
-  public ResultSet<FieldBundle> readRaw() throws OrmException {
+  public ResultSet<FieldBundle> readRaw() {
     return source.readRaw();
   }
 
   @Override
-  public ResultSet<T> restart(int start) throws OrmException {
+  public ResultSet<T> restart(int start) {
     opts = opts.withStart(start);
     try {
       source = index.getSource(pred, opts);
@@ -95,7 +95,7 @@
       // Don't need to show this exception to the user; the only thing that
       // changed about pred was its start, and any other QPEs that might happen
       // should have already thrown from the constructor.
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
     // Don't convert start to a limit, since the caller of this method (see
     // AndSource) has calculated the actual number to skip.
diff --git a/java/com/google/gerrit/index/query/IntegerRangePredicate.java b/java/com/google/gerrit/index/query/IntegerRangePredicate.java
index 66351a8..6780867 100644
--- a/java/com/google/gerrit/index/query/IntegerRangePredicate.java
+++ b/java/com/google/gerrit/index/query/IntegerRangePredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.query.RangeUtil.Range;
-import com.google.gwtorm.server.OrmException;
 
 public abstract class IntegerRangePredicate<T> extends IndexPredicate<T> {
   private final Range range;
@@ -30,9 +29,9 @@
     }
   }
 
-  protected abstract Integer getValueInt(T object) throws OrmException;
+  protected abstract Integer getValueInt(T object);
 
-  public boolean match(T object) throws OrmException {
+  public boolean match(T object) {
     Integer valueInt = getValueInt(object);
     if (valueInt == null) {
       return false;
diff --git a/java/com/google/gerrit/index/query/InternalQuery.java b/java/com/google/gerrit/index/query/InternalQuery.java
index ca5cc9b..48e214e 100644
--- a/java/com/google/gerrit/index/query/InternalQuery.java
+++ b/java/com/google/gerrit/index/query/InternalQuery.java
@@ -20,12 +20,12 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.Index;
 import com.google.gerrit.index.IndexCollection;
 import com.google.gerrit.index.IndexConfig;
 import com.google.gerrit.index.Schema;
-import com.google.gwtorm.server.OrmException;
 import java.util.Arrays;
 import java.util.List;
 import java.util.function.Supplier;
@@ -88,15 +88,15 @@
     return self();
   }
 
-  public final List<T> query(Predicate<T> p) throws OrmException {
+  public final List<T> query(Predicate<T> p) {
     return queryResults(p).entities();
   }
 
-  final QueryResult<T> queryResults(Predicate<T> p) throws OrmException {
+  final QueryResult<T> queryResults(Predicate<T> p) {
     try {
       return queryProcessor.query(p);
     } catch (QueryParseException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
   }
 
@@ -110,11 +110,11 @@
    * @return results of the queries, one list of results per input query, in the same order as the
    *     input.
    */
-  public final List<List<T>> query(List<Predicate<T>> queries) throws OrmException {
+  public final List<List<T>> query(List<Predicate<T>> queries) {
     try {
       return Lists.transform(queryProcessor.query(queries), QueryResult::entities);
     } catch (QueryParseException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
   }
 
@@ -144,11 +144,9 @@
    * @param predicate predicate to search for.
    * @param <T> result type.
    * @return exhaustive list of results, subject to the race condition described above.
-   * @throws OrmException if an error occurred.
    */
   protected static <T> ImmutableList<T> queryExhaustively(
-      Supplier<? extends InternalQuery<T, ?>> querySupplier, Predicate<T> predicate)
-      throws OrmException {
+      Supplier<? extends InternalQuery<T, ?>> querySupplier, Predicate<T> predicate) {
     ImmutableList.Builder<T> b = null;
     int start = 0;
     while (true) {
diff --git a/java/com/google/gerrit/index/query/Matchable.java b/java/com/google/gerrit/index/query/Matchable.java
index 3d07943..7a16ae8 100644
--- a/java/com/google/gerrit/index/query/Matchable.java
+++ b/java/com/google/gerrit/index/query/Matchable.java
@@ -14,15 +14,9 @@
 
 package com.google.gerrit.index.query;
 
-import com.google.gwtorm.server.OrmException;
-
 public interface Matchable<T> {
-  /**
-   * Does this predicate match this object?
-   *
-   * @throws OrmException
-   */
-  boolean match(T object) throws OrmException;
+  /** Does this predicate match this object? */
+  boolean match(T object);
 
   /** @return a cost estimate to run this predicate, higher figures cost more. */
   int getCost();
diff --git a/java/com/google/gerrit/index/query/NotPredicate.java b/java/com/google/gerrit/index/query/NotPredicate.java
index 750759d..14cb740 100644
--- a/java/com/google/gerrit/index/query/NotPredicate.java
+++ b/java/com/google/gerrit/index/query/NotPredicate.java
@@ -16,7 +16,6 @@
 
 import static com.google.common.base.Preconditions.checkState;
 
-import com.google.gwtorm.server.OrmException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -64,7 +63,7 @@
   }
 
   @Override
-  public boolean match(T object) throws OrmException {
+  public boolean match(T object) {
     checkState(
         that.isMatchable(),
         "match invoked, but child predicate %s doesn't implement %s",
diff --git a/java/com/google/gerrit/index/query/OrPredicate.java b/java/com/google/gerrit/index/query/OrPredicate.java
index 8c3ed1c..9bc3769 100644
--- a/java/com/google/gerrit/index/query/OrPredicate.java
+++ b/java/com/google/gerrit/index/query/OrPredicate.java
@@ -16,7 +16,6 @@
 
 import static com.google.common.base.Preconditions.checkState;
 
-import com.google.gwtorm.server.OrmException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -82,7 +81,7 @@
   }
 
   @Override
-  public boolean match(T object) throws OrmException {
+  public boolean match(T object) {
     for (Predicate<T> c : children) {
       checkState(
           c.isMatchable(),
diff --git a/java/com/google/gerrit/index/query/Paginated.java b/java/com/google/gerrit/index/query/Paginated.java
index c11d8c7..e61dd53 100644
--- a/java/com/google/gerrit/index/query/Paginated.java
+++ b/java/com/google/gerrit/index/query/Paginated.java
@@ -15,10 +15,9 @@
 package com.google.gerrit.index.query;
 
 import com.google.gerrit.index.QueryOptions;
-import com.google.gwtorm.server.OrmException;
 
 public interface Paginated<T> {
   QueryOptions getOptions();
 
-  ResultSet<T> restart(int start) throws OrmException;
+  ResultSet<T> restart(int start);
 }
diff --git a/java/com/google/gerrit/index/query/Predicate.java b/java/com/google/gerrit/index/query/Predicate.java
index 53c92c9..b5ed82d 100644
--- a/java/com/google/gerrit/index/query/Predicate.java
+++ b/java/com/google/gerrit/index/query/Predicate.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.index.query;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
 
 import com.google.common.collect.Iterables;
@@ -82,6 +83,8 @@
 
   /** Invert the passed node. */
   public static <T> Predicate<T> not(Predicate<T> that) {
+    checkArgument(
+        !(that instanceof Any), "negating any() is unsafe because it post-filters all results");
     if (that instanceof NotPredicate) {
       // Negate of a negate is the original predicate.
       //
diff --git a/java/com/google/gerrit/index/query/QueryBuilder.java b/java/com/google/gerrit/index/query/QueryBuilder.java
index d744fee..d24cfeb 100644
--- a/java/com/google/gerrit/index/query/QueryBuilder.java
+++ b/java/com/google/gerrit/index/query/QueryBuilder.java
@@ -31,6 +31,9 @@
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.registration.Extension;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -40,7 +43,6 @@
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import org.antlr.runtime.tree.Tree;
 
 /**
@@ -72,11 +74,14 @@
  * <p>Subclasses may also declare a handler for values which appear without operator by overriding
  * {@link #defaultField(String)}.
  *
+ * <p>Instances are non-singletons and should only be used once, in order to rescan the {@code
+ * DynamicMap} of plugin-provided operators on each query invocation.
+ *
  * @param <T> type of object the predicates can evaluate in memory.
  */
-public abstract class QueryBuilder<T> {
+public abstract class QueryBuilder<T, Q extends QueryBuilder<T, Q>> {
   /** Converts a value string passed to an operator into a {@link Predicate}. */
-  public interface OperatorFactory<T, Q extends QueryBuilder<T>> {
+  public interface OperatorFactory<T, Q extends QueryBuilder<T, Q>> {
     Predicate<T> create(Q builder, String value) throws QueryParseException;
   }
 
@@ -92,10 +97,10 @@
    * @param <T> type of object the predicates can evaluate.
    * @param <Q> type of the query builder subclass.
    */
-  public static class Definition<T, Q extends QueryBuilder<T>> {
+  public static class Definition<T, Q extends QueryBuilder<T, Q>> {
     private final ImmutableMap<String, OperatorFactory<T, Q>> opFactories;
 
-    public Definition(Class<Q> clazz) {
+    public Definition(Class<? extends Q> clazz) {
       ImmutableMap.Builder<String, OperatorFactory<T, Q>> b = ImmutableMap.builder();
       Class<?> c = clazz;
       while (c != QueryBuilder.class) {
@@ -177,14 +182,26 @@
     return null;
   }
 
-  protected final Definition<T, ? extends QueryBuilder<T>> builderDef;
+  protected final Definition<T, Q> builderDef;
+  private final ImmutableMap<String, OperatorFactory<T, Q>> opFactories;
 
-  protected final Map<String, OperatorFactory<?, ?>> opFactories;
-
-  @SuppressWarnings({"unchecked", "rawtypes"})
-  protected QueryBuilder(Definition<T, ? extends QueryBuilder<T>> def) {
+  protected QueryBuilder(
+      Definition<T, Q> def,
+      @Nullable DynamicMap<? extends OperatorFactory<T, Q>> dynamicOpFactories) {
     builderDef = def;
-    opFactories = (Map) def.opFactories;
+
+    if (dynamicOpFactories != null) {
+      ImmutableMap.Builder<String, OperatorFactory<T, Q>> opFactoriesBuilder =
+          ImmutableMap.builder();
+      opFactoriesBuilder.putAll(def.opFactories);
+      for (Extension<? extends OperatorFactory<T, Q>> e : dynamicOpFactories) {
+        String name = e.getExportName() + "_" + e.getPluginName();
+        opFactoriesBuilder.put(name, e.getProvider().get());
+      }
+      opFactories = opFactoriesBuilder.build();
+    } else {
+      opFactories = def.opFactories;
+    }
   }
 
   /**
@@ -324,7 +341,7 @@
   @Target(ElementType.METHOD)
   protected @interface Operator {}
 
-  private static class ReflectionFactory<T, Q extends QueryBuilder<T>>
+  private static class ReflectionFactory<T, Q extends QueryBuilder<T, Q>>
       implements OperatorFactory<T, Q> {
     private final String name;
     private final Method method;
diff --git a/java/com/google/gerrit/index/query/QueryProcessor.java b/java/com/google/gerrit/index/query/QueryProcessor.java
index c68f674..7077245 100644
--- a/java/com/google/gerrit/index/query/QueryProcessor.java
+++ b/java/com/google/gerrit/index/query/QueryProcessor.java
@@ -26,6 +26,7 @@
 import com.google.common.collect.Ordering;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.Index;
 import com.google.gerrit.index.IndexCollection;
 import com.google.gerrit.index.IndexConfig;
@@ -37,8 +38,6 @@
 import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.metrics.Timer1;
 import com.google.gerrit.server.logging.CallerFinder;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
@@ -173,7 +172,7 @@
    * @param query the query.
    * @return results of the query.
    */
-  public QueryResult<T> query(Predicate<T> query) throws OrmException, QueryParseException {
+  public QueryResult<T> query(Predicate<T> query) throws QueryParseException {
     return query(ImmutableList.of(query)).get(0);
   }
 
@@ -188,13 +187,10 @@
    * @return results of the queries, one QueryResult per input query, in the same order as the
    *     input.
    */
-  public List<QueryResult<T>> query(List<Predicate<T>> queries)
-      throws OrmException, QueryParseException {
+  public List<QueryResult<T>> query(List<Predicate<T>> queries) throws QueryParseException {
     try {
       return query(null, queries);
-    } catch (OrmRuntimeException e) {
-      throw new OrmException(e.getMessage(), e);
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       if (e.getCause() != null) {
         Throwables.throwIfInstanceOf(e.getCause(), QueryParseException.class);
       }
@@ -203,8 +199,7 @@
   }
 
   private List<QueryResult<T>> query(
-      @Nullable List<String> queryStrings, List<Predicate<T>> queries)
-      throws OrmException, QueryParseException {
+      @Nullable List<String> queryStrings, List<Predicate<T>> queries) throws QueryParseException {
     long startNanos = System.nanoTime();
     checkState(!used.getAndSet(true), "%s has already been used", getClass().getSimpleName());
     int cnt = queries.size();
@@ -287,7 +282,7 @@
       // Only measure successful queries that actually touched the index.
       metrics.executionTime.record(
           schemaDef.getName(), System.nanoTime() - startNanos, TimeUnit.NANOSECONDS);
-    } catch (OrmException | OrmRuntimeException e) {
+    } catch (StorageException e) {
       Optional<QueryParseException> qpe = findQueryParseException(e);
       if (qpe.isPresent()) {
         throw new QueryParseException(qpe.get().getMessage(), e);
diff --git a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
index ae36b48..7a0430c 100644
--- a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
+++ b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
@@ -31,6 +31,7 @@
 import com.google.common.util.concurrent.MoreExecutors;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.FieldType;
 import com.google.gerrit.index.Index;
@@ -45,7 +46,6 @@
 import com.google.gerrit.server.index.IndexUtils;
 import com.google.gerrit.server.logging.LoggingContextAwareExecutorService;
 import com.google.gerrit.server.logging.LoggingContextAwareScheduledExecutorService;
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 import java.sql.Timestamp;
 import java.util.Map;
@@ -213,7 +213,7 @@
   }
 
   @Override
-  public void markReady(boolean ready) throws IOException {
+  public void markReady(boolean ready) {
     IndexUtils.setReady(sitePaths, name, schema.getVersion(), ready);
   }
 
@@ -287,8 +287,12 @@
   }
 
   @Override
-  public void deleteAll() throws IOException {
-    writer.deleteAll();
+  public void deleteAll() {
+    try {
+      writer.deleteAll();
+    } catch (IOException e) {
+      throw new StorageException(e);
+    }
   }
 
   public IndexWriter getWriter() {
@@ -486,16 +490,16 @@
     }
 
     @Override
-    public ResultSet<V> read() throws OrmException {
+    public ResultSet<V> read() {
       return readImpl(AbstractLuceneIndex.this::fromDocument);
     }
 
     @Override
-    public ResultSet<FieldBundle> readRaw() throws OrmException {
+    public ResultSet<FieldBundle> readRaw() {
       return readImpl(AbstractLuceneIndex.this::toFieldBundle);
     }
 
-    private <T> ResultSet<T> readImpl(Function<Document, T> mapper) throws OrmException {
+    private <T> ResultSet<T> readImpl(Function<Document, T> mapper) {
       IndexSearcher searcher = null;
       try {
         searcher = acquire();
@@ -512,7 +516,7 @@
         }
         return new ListResultSet<>(b.build());
       } catch (IOException e) {
-        throw new OrmException(e);
+        throw new StorageException(e);
       } finally {
         if (searcher != null) {
           try {
diff --git a/java/com/google/gerrit/lucene/BUILD b/java/com/google/gerrit/lucene/BUILD
index 3b18f2c..fa4c923 100644
--- a/java/com/google/gerrit/lucene/BUILD
+++ b/java/com/google/gerrit/lucene/BUILD
@@ -10,7 +10,6 @@
         "//java/com/google/gerrit/reviewdb:server",
         "//java/com/google/gerrit/server",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib/lucene:lucene-core-and-backward-codecs",
     ],
 )
@@ -26,6 +25,7 @@
         ":query_builder",
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/index",
         "//java/com/google/gerrit/index:query_exception",
@@ -36,7 +36,6 @@
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/server/logging",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:protobuf",
         "//lib/flogger:api",
         "//lib/guice",
diff --git a/java/com/google/gerrit/lucene/ChangeSubIndex.java b/java/com/google/gerrit/lucene/ChangeSubIndex.java
index 7d7cbef..98424b5 100644
--- a/java/com/google/gerrit/lucene/ChangeSubIndex.java
+++ b/java/com/google/gerrit/lucene/ChangeSubIndex.java
@@ -71,12 +71,12 @@
   }
 
   @Override
-  public void replace(ChangeData obj) throws IOException {
+  public void replace(ChangeData obj) {
     throw new UnsupportedOperationException("don't use ChangeSubIndex directly");
   }
 
   @Override
-  public void delete(Change.Id key) throws IOException {
+  public void delete(Change.Id key) {
     throw new UnsupportedOperationException("don't use ChangeSubIndex directly");
   }
 
diff --git a/java/com/google/gerrit/lucene/LuceneAccountIndex.java b/java/com/google/gerrit/lucene/LuceneAccountIndex.java
index 86a2111..0b787b6 100644
--- a/java/com/google/gerrit/lucene/LuceneAccountIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneAccountIndex.java
@@ -19,6 +19,7 @@
 import static com.google.gerrit.server.index.account.AccountField.ID;
 import static com.google.gerrit.server.index.account.AccountField.PREFERRED_EMAIL_EXACT;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
@@ -120,20 +121,20 @@
   }
 
   @Override
-  public void replace(AccountState as) throws IOException {
+  public void replace(AccountState as) {
     try {
       replace(idTerm(as), toDocument(as)).get();
     } catch (ExecutionException | InterruptedException e) {
-      throw new IOException(e);
+      throw new StorageException(e);
     }
   }
 
   @Override
-  public void delete(Account.Id key) throws IOException {
+  public void delete(Account.Id key) {
     try {
       delete(idTerm(key)).get();
     } catch (ExecutionException | InterruptedException e) {
-      throw new IOException(e);
+      throw new StorageException(e);
     }
   }
 
diff --git a/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index 0e34c93..8bf0b6b 100644
--- a/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -36,6 +36,7 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.query.FieldBundle;
@@ -62,8 +63,6 @@
 import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeDataSource;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import com.google.protobuf.MessageLite;
@@ -201,7 +200,7 @@
   }
 
   @Override
-  public void replace(ChangeData cd) throws IOException {
+  public void replace(ChangeData cd) {
     Term id = LuceneChangeIndex.idTerm(cd);
     // toDocument is essentially static and doesn't depend on the specific
     // sub-index, so just pick one.
@@ -212,23 +211,23 @@
       } else {
         Futures.allAsList(openIndex.delete(id), closedIndex.replace(id, doc)).get();
       }
-    } catch (OrmException | ExecutionException | InterruptedException e) {
-      throw new IOException(e);
+    } catch (ExecutionException | InterruptedException e) {
+      throw new StorageException(e);
     }
   }
 
   @Override
-  public void delete(Change.Id id) throws IOException {
+  public void delete(Change.Id id) {
     Term idTerm = LuceneChangeIndex.idTerm(id);
     try {
       Futures.allAsList(openIndex.delete(idTerm), closedIndex.delete(idTerm)).get();
     } catch (ExecutionException | InterruptedException e) {
-      throw new IOException(e);
+      throw new StorageException(e);
     }
   }
 
   @Override
-  public void deleteAll() throws IOException {
+  public void deleteAll() {
     openIndex.deleteAll();
     closedIndex.deleteAll();
   }
@@ -248,7 +247,7 @@
   }
 
   @Override
-  public void markReady(boolean ready) throws IOException {
+  public void markReady(boolean ready) {
     // Arbitrary done on open index, as ready bit is set
     // per index and not sub index
     openIndex.markReady(ready);
@@ -303,10 +302,10 @@
     }
 
     @Override
-    public ResultSet<ChangeData> read() throws OrmException {
+    public ResultSet<ChangeData> read() {
       if (Thread.interrupted()) {
         Thread.currentThread().interrupt();
-        throw new OrmException("interrupted");
+        throw new StorageException("interrupted");
       }
 
       final Set<String> fields = IndexUtils.changeFields(opts);
@@ -327,12 +326,12 @@
     }
 
     @Override
-    public ResultSet<FieldBundle> readRaw() throws OrmException {
+    public ResultSet<FieldBundle> readRaw() {
       List<Document> documents;
       try {
         documents = doRead(IndexUtils.changeFields(opts));
       } catch (IOException e) {
-        throw new OrmException(e);
+        throw new StorageException(e);
       }
       ImmutableList<FieldBundle> fieldBundles =
           documents.stream().map(rawDocumentMapper).collect(toImmutableList());
@@ -415,10 +414,10 @@
         return result.build();
       } catch (InterruptedException e) {
         close();
-        throw new OrmRuntimeException(e);
+        throw new StorageException(e);
       } catch (ExecutionException e) {
         Throwables.throwIfUnchecked(e.getCause());
-        throw new OrmRuntimeException(e.getCause());
+        throw new StorageException(e.getCause());
       }
     }
 
diff --git a/java/com/google/gerrit/lucene/LuceneGroupIndex.java b/java/com/google/gerrit/lucene/LuceneGroupIndex.java
index 95e2ab9..0fdef77 100644
--- a/java/com/google/gerrit/lucene/LuceneGroupIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneGroupIndex.java
@@ -17,6 +17,7 @@
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static com.google.gerrit.server.index.group.GroupField.UUID;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
@@ -110,20 +111,20 @@
   }
 
   @Override
-  public void replace(InternalGroup group) throws IOException {
+  public void replace(InternalGroup group) {
     try {
       replace(idTerm(group), toDocument(group)).get();
     } catch (ExecutionException | InterruptedException e) {
-      throw new IOException(e);
+      throw new StorageException(e);
     }
   }
 
   @Override
-  public void delete(AccountGroup.UUID key) throws IOException {
+  public void delete(AccountGroup.UUID key) {
     try {
       delete(idTerm(key)).get();
     } catch (ExecutionException | InterruptedException e) {
-      throw new IOException(e);
+      throw new StorageException(e);
     }
   }
 
diff --git a/java/com/google/gerrit/lucene/LuceneProjectIndex.java b/java/com/google/gerrit/lucene/LuceneProjectIndex.java
index 02d8655..950e206 100644
--- a/java/com/google/gerrit/lucene/LuceneProjectIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneProjectIndex.java
@@ -17,6 +17,7 @@
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static com.google.gerrit.index.project.ProjectField.NAME;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
@@ -110,20 +111,20 @@
   }
 
   @Override
-  public void replace(ProjectData projectState) throws IOException {
+  public void replace(ProjectData projectState) {
     try {
       replace(idTerm(projectState), toDocument(projectState)).get();
     } catch (ExecutionException | InterruptedException e) {
-      throw new IOException(e);
+      throw new StorageException(e);
     }
   }
 
   @Override
-  public void delete(Project.NameKey nameKey) throws IOException {
+  public void delete(Project.NameKey nameKey) {
     try {
       delete(idTerm(nameKey)).get();
     } catch (ExecutionException | InterruptedException e) {
-      throw new IOException(e);
+      throw new StorageException(e);
     }
   }
 
diff --git a/java/com/google/gerrit/pgm/BUILD b/java/com/google/gerrit/pgm/BUILD
index 0bebad4..02c083c 100644
--- a/java/com/google/gerrit/pgm/BUILD
+++ b/java/com/google/gerrit/pgm/BUILD
@@ -18,6 +18,7 @@
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/elasticsearch",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/gpg",
         "//java/com/google/gerrit/httpd",
@@ -49,7 +50,6 @@
         "//java/com/google/gerrit/util/http",
         "//lib:args4j",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:protobuf",
         "//lib:servlet-api-3_1-without-neverlink",
         "//lib/auto:auto-value",
diff --git a/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java b/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
index 14a0b5d..e6e091c 100644
--- a/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
+++ b/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
@@ -16,6 +16,7 @@
 
 import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_GERRIT;
 
+import com.google.gerrit.exceptions.DuplicateKeyException;
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.lifecycle.LifecycleManager;
 import com.google.gerrit.pgm.util.SiteProgram;
@@ -29,7 +30,6 @@
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.index.account.AccountSchemaDefinitions;
 import com.google.gerrit.server.schema.NoteDbSchemaVersionCheck;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.google.inject.Provider;
@@ -96,7 +96,7 @@
   }
 
   private void convertLocalUserToLowerCase(ExternalIdNotes extIdNotes, ExternalId extId)
-      throws OrmDuplicateKeyException, IOException {
+      throws DuplicateKeyException, IOException {
     if (extId.isScheme(SCHEME_GERRIT)) {
       String localUser = extId.key().id();
       String localUserLowerCase = localUser.toLowerCase(Locale.US);
diff --git a/java/com/google/gerrit/pgm/init/BUILD b/java/com/google/gerrit/pgm/init/BUILD
index 3c6df14..b2a4d72 100644
--- a/java/com/google/gerrit/pgm/init/BUILD
+++ b/java/com/google/gerrit/pgm/init/BUILD
@@ -8,6 +8,7 @@
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/elasticsearch",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/index",
         "//java/com/google/gerrit/launcher",
@@ -21,7 +22,6 @@
         "//java/com/google/gerrit/server/schema",
         "//java/com/google/gerrit/server/util/time",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:h2",
         "//lib/commons:validator",
         "//lib/flogger:api",
diff --git a/java/com/google/gerrit/pgm/init/BaseInit.java b/java/com/google/gerrit/pgm/init/BaseInit.java
index c1fd1df..9c158b7 100644
--- a/java/com/google/gerrit/pgm/init/BaseInit.java
+++ b/java/com/google/gerrit/pgm/init/BaseInit.java
@@ -44,7 +44,6 @@
 import com.google.gerrit.server.securestore.SecureStore;
 import com.google.gerrit.server.securestore.SecureStoreClassName;
 import com.google.gerrit.server.securestore.SecureStoreProvider;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.AbstractModule;
 import com.google.inject.CreationException;
 import com.google.inject.Guice;
@@ -350,7 +349,7 @@
       this.repositoryManager = repositoryManager;
     }
 
-    void upgradeSchema() throws OrmException {
+    void upgradeSchema() {
       noteDbSchemaUpdater.update(new UpdateUIImpl(ui));
     }
 
diff --git a/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java b/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java
index 6336c93..9519653 100644
--- a/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java
+++ b/java/com/google/gerrit/pgm/init/ExternalIdsOnInit.java
@@ -23,7 +23,6 @@
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.File;
 import java.io.IOException;
@@ -49,7 +48,7 @@
   }
 
   public synchronized void insert(String commitMessage, Collection<ExternalId> extIds)
-      throws OrmException, IOException, ConfigInvalidException {
+      throws IOException, ConfigInvalidException {
     File path = getPath();
     if (path != null) {
       try (Repository allUsersRepo = new FileRepository(path)) {
diff --git a/java/com/google/gerrit/pgm/init/GroupsOnInit.java b/java/com/google/gerrit/pgm/init/GroupsOnInit.java
index 584d8af..273ebfb 100644
--- a/java/com/google/gerrit/pgm/init/GroupsOnInit.java
+++ b/java/com/google/gerrit/pgm/init/GroupsOnInit.java
@@ -20,7 +20,7 @@
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.pgm.init.api.AllUsersNameOnInitProvider;
 import com.google.gerrit.pgm.init.api.InitFlags;
 import com.google.gerrit.reviewdb.client.Account;
diff --git a/java/com/google/gerrit/pgm/init/InitAdminUser.java b/java/com/google/gerrit/pgm/init/InitAdminUser.java
index f19cf39..27e6ce9 100644
--- a/java/com/google/gerrit/pgm/init/InitAdminUser.java
+++ b/java/com/google/gerrit/pgm/init/InitAdminUser.java
@@ -18,7 +18,7 @@
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.client.AuthType;
 import com.google.gerrit.pgm.init.api.AllUsersNameOnInitProvider;
 import com.google.gerrit.pgm.init.api.ConsoleUI;
diff --git a/java/com/google/gerrit/pgm/init/api/BUILD b/java/com/google/gerrit/pgm/init/api/BUILD
index bc418dd..5b07fc6 100644
--- a/java/com/google/gerrit/pgm/init/api/BUILD
+++ b/java/com/google/gerrit/pgm/init/api/BUILD
@@ -5,10 +5,10 @@
     deps = [
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/reviewdb:server",
         "//java/com/google/gerrit/server",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib/flogger:api",
         "//lib/guice",
         "//lib/guice:guice-assistedinject",
diff --git a/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java b/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
index aa9fca2..d3d22cb 100644
--- a/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
+++ b/java/com/google/gerrit/pgm/init/api/SequencesOnInit.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.RepoSequence;
 import com.google.gerrit.server.notedb.Sequences;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -34,7 +33,7 @@
     this.allUsersName = allUsersName;
   }
 
-  public int nextAccountId() throws OrmException {
+  public int nextAccountId() {
     RepoSequence accountSeq =
         new RepoSequence(
             repoManager,
diff --git a/java/com/google/gerrit/pgm/util/BUILD b/java/com/google/gerrit/pgm/util/BUILD
index defc4d3..ffd1cbd 100644
--- a/java/com/google/gerrit/pgm/util/BUILD
+++ b/java/com/google/gerrit/pgm/util/BUILD
@@ -19,7 +19,6 @@
         "//java/com/google/gerrit/util/cli",
         "//lib:args4j",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib/flogger:api",
         "//lib/guice",
         "//lib/jgit/org.eclipse.jgit:jgit",
diff --git a/java/com/google/gerrit/pgm/util/SiteProgram.java b/java/com/google/gerrit/pgm/util/SiteProgram.java
index de8238d..7c384e7 100644
--- a/java/com/google/gerrit/pgm/util/SiteProgram.java
+++ b/java/com/google/gerrit/pgm/util/SiteProgram.java
@@ -47,7 +47,7 @@
       aliases = {"-d"},
       usage = "Local directory containing site data")
   private void setSitePath(String path) {
-    sitePath = Paths.get(path);
+    sitePath = Paths.get(path).normalize();
   }
 
   private Path sitePath = Paths.get(".");
@@ -55,7 +55,7 @@
   protected SiteProgram() {}
 
   protected SiteProgram(Path sitePath) {
-    this.sitePath = sitePath;
+    this.sitePath = sitePath.normalize();
   }
 
   /** @return the site path specified on the command line. */
diff --git a/java/com/google/gerrit/reviewdb/BUILD b/java/com/google/gerrit/reviewdb/BUILD
index 74c3403..588c47a 100644
--- a/java/com/google/gerrit/reviewdb/BUILD
+++ b/java/com/google/gerrit/reviewdb/BUILD
@@ -8,8 +8,8 @@
     deps = [
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/extensions:api",
+        "//java/com/google/gwtorm",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:protobuf",
         "//proto:entities_java_proto",
     ],
diff --git a/java/com/google/gerrit/server/ApprovalCopier.java b/java/com/google/gerrit/server/ApprovalCopier.java
index 45588a3..a1df711 100644
--- a/java/com/google/gerrit/server/ApprovalCopier.java
+++ b/java/com/google/gerrit/server/ApprovalCopier.java
@@ -22,6 +22,7 @@
 import com.google.common.collect.Table;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.ChangeKind;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.PatchSet;
@@ -32,7 +33,6 @@
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -73,8 +73,7 @@
   }
 
   Iterable<PatchSetApproval> getForPatchSet(
-      ChangeNotes notes, PatchSet.Id psId, @Nullable RevWalk rw, @Nullable Config repoConfig)
-      throws OrmException {
+      ChangeNotes notes, PatchSet.Id psId, @Nullable RevWalk rw, @Nullable Config repoConfig) {
     return getForPatchSet(notes, psId, rw, repoConfig, Collections.emptyList());
   }
 
@@ -83,8 +82,7 @@
       PatchSet.Id psId,
       @Nullable RevWalk rw,
       @Nullable Config repoConfig,
-      Iterable<PatchSetApproval> dontCopy)
-      throws OrmException {
+      Iterable<PatchSetApproval> dontCopy) {
     PatchSet ps = psUtil.get(notes, psId);
     if (ps == null) {
       return Collections.emptyList();
@@ -97,8 +95,7 @@
       PatchSet ps,
       @Nullable RevWalk rw,
       @Nullable Config repoConfig,
-      Iterable<PatchSetApproval> dontCopy)
-      throws OrmException {
+      Iterable<PatchSetApproval> dontCopy) {
     requireNonNull(ps, "ps should not be null");
     ChangeData cd = changeDataFactory.create(notes);
     try {
@@ -153,11 +150,11 @@
       }
       return labelNormalizer.normalize(notes, byUser.values()).getNormalized();
     } catch (IOException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
   }
 
-  private static TreeMap<Integer, PatchSet> getPatchSets(ChangeData cd) throws OrmException {
+  private static TreeMap<Integer, PatchSet> getPatchSets(ChangeData cd) {
     Collection<PatchSet> patchSets = cd.patchSets();
     TreeMap<Integer, PatchSet> result = new TreeMap<>();
     for (PatchSet ps : patchSets) {
diff --git a/java/com/google/gerrit/server/ApprovalsUtil.java b/java/com/google/gerrit/server/ApprovalsUtil.java
index 1b5983a4..865e33c 100644
--- a/java/com/google/gerrit/server/ApprovalsUtil.java
+++ b/java/com/google/gerrit/server/ApprovalsUtil.java
@@ -29,6 +29,7 @@
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.RestApiException;
@@ -47,7 +48,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.util.LabelVote;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -112,9 +112,8 @@
    *
    * @param notes change notes.
    * @return reviewers for the change.
-   * @throws OrmException if reviewers for the change could not be read.
    */
-  public ReviewerSet getReviewers(ChangeNotes notes) throws OrmException {
+  public ReviewerSet getReviewers(ChangeNotes notes) {
     return notes.load().getReviewers();
   }
 
@@ -123,10 +122,8 @@
    *
    * @param allApprovals all approvals to consider; must all belong to the same change.
    * @return reviewers for the change.
-   * @throws OrmException if reviewers for the change could not be read.
    */
-  public ReviewerSet getReviewers(ChangeNotes notes, Iterable<PatchSetApproval> allApprovals)
-      throws OrmException {
+  public ReviewerSet getReviewers(ChangeNotes notes, Iterable<PatchSetApproval> allApprovals) {
     return notes.load().getReviewers();
   }
 
@@ -135,9 +132,8 @@
    *
    * @param notes change notes.
    * @return reviewer updates for the change.
-   * @throws OrmException if reviewer updates for the change could not be read.
    */
-  public List<ReviewerStatusUpdate> getReviewerUpdates(ChangeNotes notes) throws OrmException {
+  public List<ReviewerStatusUpdate> getReviewerUpdates(ChangeNotes notes) {
     return notes.load().getReviewerUpdates();
   }
 
@@ -165,8 +161,7 @@
       ChangeUpdate update,
       LabelTypes labelTypes,
       Change change,
-      Iterable<Account.Id> wantReviewers)
-      throws OrmException {
+      Iterable<Account.Id> wantReviewers) {
     PatchSet.Id psId = change.currentPatchSetId();
     Collection<Account.Id> existingReviewers;
     existingReviewers = notes.load().getReviewers().byState(REVIEWER);
@@ -245,10 +240,9 @@
    * @param update change update.
    * @param wantCCs accounts to CC.
    * @return whether a change was made.
-   * @throws OrmException
    */
   public Collection<Account.Id> addCcs(
-      ChangeNotes notes, ChangeUpdate update, Collection<Account.Id> wantCCs) throws OrmException {
+      ChangeNotes notes, ChangeUpdate update, Collection<Account.Id> wantCCs) {
     return addCcs(update, wantCCs, notes.load().getReviewers());
   }
 
@@ -272,7 +266,6 @@
    * @param user user adding approvals.
    * @param approvals approvals to add.
    * @throws RestApiException
-   * @throws OrmException
    */
   public Iterable<PatchSetApproval> addApprovalsForNewPatchSet(
       ChangeUpdate update,
@@ -280,7 +273,7 @@
       PatchSet ps,
       CurrentUser user,
       Map<String, Short> approvals)
-      throws RestApiException, OrmException, PermissionBackendException {
+      throws RestApiException, PermissionBackendException {
     Account.Id accountId = user.getAccountId();
     checkArgument(
         accountId.equals(ps.getUploader()),
@@ -330,14 +323,12 @@
     }
   }
 
-  public ListMultimap<PatchSet.Id, PatchSetApproval> byChange(ChangeNotes notes)
-      throws OrmException {
+  public ListMultimap<PatchSet.Id, PatchSetApproval> byChange(ChangeNotes notes) {
     return notes.load().getApprovals();
   }
 
   public Iterable<PatchSetApproval> byPatchSet(
-      ChangeNotes notes, PatchSet.Id psId, @Nullable RevWalk rw, @Nullable Config repoConfig)
-      throws OrmException {
+      ChangeNotes notes, PatchSet.Id psId, @Nullable RevWalk rw, @Nullable Config repoConfig) {
     return copier.getForPatchSet(notes, psId, rw, repoConfig);
   }
 
@@ -346,8 +337,7 @@
       PatchSet.Id psId,
       Account.Id accountId,
       @Nullable RevWalk rw,
-      @Nullable Config repoConfig)
-      throws OrmException {
+      @Nullable Config repoConfig) {
     return filterApprovals(byPatchSet(notes, psId, rw, repoConfig), accountId);
   }
 
@@ -358,7 +348,7 @@
     try {
       // Submit approval is never copied, so bypass expensive byPatchSet call.
       return getSubmitter(c, byChange(notes).get(c));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       return null;
     }
   }
diff --git a/java/com/google/gerrit/server/BUILD b/java/com/google/gerrit/server/BUILD
index 2634048..34e9f3a 100644
--- a/java/com/google/gerrit/server/BUILD
+++ b/java/com/google/gerrit/server/BUILD
@@ -33,6 +33,7 @@
         ":constants",
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/git",
         "//java/com/google/gerrit/index",
@@ -52,6 +53,7 @@
         "//java/com/google/gerrit/server/util/time",
         "//java/com/google/gerrit/util/cli",
         "//java/com/google/gerrit/util/ssl",
+        "//java/com/google/gwtorm",
         "//java/org/apache/commons/net",
         "//java/org/eclipse/jgit:server",
         "//lib:args4j",
@@ -86,7 +88,6 @@
         "//lib:gson",
         "//lib:guava",
         "//lib:guava-retrying",
-        "//lib:gwtorm",
         "//lib:jsch",
         "//lib:juniversalchardet",
         "//lib:mime-util",
diff --git a/java/com/google/gerrit/server/ChangeMessagesUtil.java b/java/com/google/gerrit/server/ChangeMessagesUtil.java
index 214c5d7..97ba8f0 100644
--- a/java/com/google/gerrit/server/ChangeMessagesUtil.java
+++ b/java/com/google/gerrit/server/ChangeMessagesUtil.java
@@ -26,7 +26,6 @@
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.ChangeUpdate;
 import com.google.gerrit.server.update.ChangeContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Singleton;
 import java.sql.Timestamp;
 import java.util.List;
@@ -87,7 +86,7 @@
     return workInProgress ? TAG_UPLOADED_WIP_PATCH_SET : TAG_UPLOADED_PATCH_SET;
   }
 
-  public List<ChangeMessage> byChange(ChangeNotes notes) throws OrmException {
+  public List<ChangeMessage> byChange(ChangeNotes notes) {
     return notes.load().getChangeMessages();
   }
 
diff --git a/java/com/google/gerrit/server/CommentsUtil.java b/java/com/google/gerrit/server/CommentsUtil.java
index 1229df1..a5332eb 100644
--- a/java/com/google/gerrit/server/CommentsUtil.java
+++ b/java/com/google/gerrit/server/CommentsUtil.java
@@ -23,6 +23,7 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Ordering;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.Side;
 import com.google.gerrit.extensions.common.CommentInfo;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
@@ -42,7 +43,6 @@
 import com.google.gerrit.server.patch.PatchListCache;
 import com.google.gerrit.server.patch.PatchListNotAvailableException;
 import com.google.gerrit.server.update.ChangeContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -124,7 +124,7 @@
       String message,
       @Nullable Boolean unresolved,
       @Nullable String parentUuid)
-      throws OrmException, UnprocessableEntityException {
+      throws UnprocessableEntityException {
     if (unresolved == null) {
       if (parentUuid == null) {
         // Default to false if comment is not descended from another.
@@ -175,28 +175,27 @@
     return c;
   }
 
-  public Optional<Comment> getPublished(ChangeNotes notes, Comment.Key key) throws OrmException {
+  public Optional<Comment> getPublished(ChangeNotes notes, Comment.Key key) {
     return publishedByChange(notes).stream().filter(c -> key.equals(c.key)).findFirst();
   }
 
-  public Optional<Comment> getDraft(ChangeNotes notes, IdentifiedUser user, Comment.Key key)
-      throws OrmException {
+  public Optional<Comment> getDraft(ChangeNotes notes, IdentifiedUser user, Comment.Key key) {
     return draftByChangeAuthor(notes, user.getAccountId()).stream()
         .filter(c -> key.equals(c.key))
         .findFirst();
   }
 
-  public List<Comment> publishedByChange(ChangeNotes notes) throws OrmException {
+  public List<Comment> publishedByChange(ChangeNotes notes) {
     notes.load();
     return sort(Lists.newArrayList(notes.getComments().values()));
   }
 
-  public List<RobotComment> robotCommentsByChange(ChangeNotes notes) throws OrmException {
+  public List<RobotComment> robotCommentsByChange(ChangeNotes notes) {
     notes.load();
     return sort(Lists.newArrayList(notes.getRobotComments().values()));
   }
 
-  public List<Comment> draftByChange(ChangeNotes notes) throws OrmException {
+  public List<Comment> draftByChange(ChangeNotes notes) {
     List<Comment> comments = new ArrayList<>();
     for (Ref ref : getDraftRefs(notes.getChangeId())) {
       Account.Id account = Account.Id.fromRefSuffix(ref.getName());
@@ -207,7 +206,7 @@
     return sort(comments);
   }
 
-  public List<Comment> byPatchSet(ChangeNotes notes, PatchSet.Id psId) throws OrmException {
+  public List<Comment> byPatchSet(ChangeNotes notes, PatchSet.Id psId) {
     List<Comment> comments = new ArrayList<>();
     comments.addAll(publishedByPatchSet(notes, psId));
 
@@ -220,18 +219,16 @@
     return sort(comments);
   }
 
-  public List<Comment> publishedByChangeFile(ChangeNotes notes, String file) throws OrmException {
+  public List<Comment> publishedByChangeFile(ChangeNotes notes, String file) {
     return commentsOnFile(notes.load().getComments().values(), file);
   }
 
-  public List<Comment> publishedByPatchSet(ChangeNotes notes, PatchSet.Id psId)
-      throws OrmException {
+  public List<Comment> publishedByPatchSet(ChangeNotes notes, PatchSet.Id psId) {
     return removeCommentsOnAncestorOfCommitMessage(
         commentsOnPatchSet(notes.load().getComments().values(), psId));
   }
 
-  public List<RobotComment> robotCommentsByPatchSet(ChangeNotes notes, PatchSet.Id psId)
-      throws OrmException {
+  public List<RobotComment> robotCommentsByPatchSet(ChangeNotes notes, PatchSet.Id psId) {
     return commentsOnPatchSet(notes.load().getRobotComments().values(), psId);
   }
 
@@ -248,18 +245,16 @@
         .collect(toList());
   }
 
-  public List<Comment> draftByPatchSetAuthor(PatchSet.Id psId, Account.Id author, ChangeNotes notes)
-      throws OrmException {
+  public List<Comment> draftByPatchSetAuthor(
+      PatchSet.Id psId, Account.Id author, ChangeNotes notes) {
     return commentsOnPatchSet(notes.load().getDraftComments(author).values(), psId);
   }
 
-  public List<Comment> draftByChangeFileAuthor(ChangeNotes notes, String file, Account.Id author)
-      throws OrmException {
+  public List<Comment> draftByChangeFileAuthor(ChangeNotes notes, String file, Account.Id author) {
     return commentsOnFile(notes.load().getDraftComments(author).values(), file);
   }
 
-  public List<Comment> draftByChangeAuthor(ChangeNotes notes, Account.Id author)
-      throws OrmException {
+  public List<Comment> draftByChangeAuthor(ChangeNotes notes, Account.Id author) {
     List<Comment> comments = new ArrayList<>();
     comments.addAll(notes.getDraftComments(author).values());
     return sort(comments);
@@ -343,11 +338,11 @@
    * @param changeId change ID.
    * @return raw refs from All-Users repo.
    */
-  public Collection<Ref> getDraftRefs(Change.Id changeId) throws OrmException {
+  public Collection<Ref> getDraftRefs(Change.Id changeId) {
     try (Repository repo = repoManager.openRepository(allUsers)) {
       return getDraftRefs(repo, changeId);
     } catch (IOException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/PatchSetUtil.java b/java/com/google/gerrit/server/PatchSetUtil.java
index e7f1d2a..2a78eb6 100644
--- a/java/com/google/gerrit/server/PatchSetUtil.java
+++ b/java/com/google/gerrit/server/PatchSetUtil.java
@@ -33,7 +33,6 @@
 import com.google.gerrit.server.notedb.ChangeUpdate;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -63,24 +62,24 @@
     this.repoManager = repoManager;
   }
 
-  public PatchSet current(ChangeNotes notes) throws OrmException {
+  public PatchSet current(ChangeNotes notes) {
     return get(notes, notes.getChange().currentPatchSetId());
   }
 
-  public PatchSet get(ChangeNotes notes, PatchSet.Id psId) throws OrmException {
+  public PatchSet get(ChangeNotes notes, PatchSet.Id psId) {
     return notes.load().getPatchSets().get(psId);
   }
 
-  public ImmutableCollection<PatchSet> byChange(ChangeNotes notes) throws OrmException {
+  public ImmutableCollection<PatchSet> byChange(ChangeNotes notes) {
     return notes.load().getPatchSets().values();
   }
 
-  public ImmutableMap<PatchSet.Id, PatchSet> byChangeAsMap(ChangeNotes notes) throws OrmException {
+  public ImmutableMap<PatchSet.Id, PatchSet> byChangeAsMap(ChangeNotes notes) {
     return notes.load().getPatchSets();
   }
 
   public ImmutableMap<PatchSet.Id, PatchSet> getAsMap(
-      ChangeNotes notes, Set<PatchSet.Id> patchSetIds) throws OrmException {
+      ChangeNotes notes, Set<PatchSet.Id> patchSetIds) {
     return ImmutableMap.copyOf(Maps.filterKeys(notes.load().getPatchSets(), patchSetIds::contains));
   }
 
@@ -135,7 +134,7 @@
 
   /** Check if the current patch set of the change is locked. */
   public void checkPatchSetNotLocked(ChangeNotes notes)
-      throws OrmException, IOException, ResourceConflictException {
+      throws IOException, ResourceConflictException {
     if (isPatchSetLocked(notes)) {
       throw new ResourceConflictException(
           String.format("The current patch set of change %s is locked", notes.getChangeId()));
@@ -143,7 +142,7 @@
   }
 
   /** Is the current patch set locked against state changes? */
-  public boolean isPatchSetLocked(ChangeNotes notes) throws OrmException, IOException {
+  public boolean isPatchSetLocked(ChangeNotes notes) throws IOException {
     Change change = notes.getChange();
     if (change.isMerged()) {
       return false;
diff --git a/java/com/google/gerrit/server/PublishCommentUtil.java b/java/com/google/gerrit/server/PublishCommentUtil.java
index 75a364c..ad93ef0 100644
--- a/java/com/google/gerrit/server/PublishCommentUtil.java
+++ b/java/com/google/gerrit/server/PublishCommentUtil.java
@@ -19,13 +19,13 @@
 import static java.util.stream.Collectors.toSet;
 
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.patch.PatchListCache;
 import com.google.gerrit.server.patch.PatchListNotAvailableException;
 import com.google.gerrit.server.update.ChangeContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.Collection;
@@ -46,8 +46,7 @@
   }
 
   public void publish(
-      ChangeContext ctx, PatchSet.Id psId, Collection<Comment> drafts, @Nullable String tag)
-      throws OrmException {
+      ChangeContext ctx, PatchSet.Id psId, Collection<Comment> drafts, @Nullable String tag) {
     ChangeNotes notes = ctx.getNotes();
     checkArgument(notes != null);
     if (drafts.isEmpty()) {
@@ -59,7 +58,7 @@
     for (Comment d : drafts) {
       PatchSet ps = patchSets.get(psId(notes, d));
       if (ps == null) {
-        throw new OrmException("patch set " + ps + " not found");
+        throw new StorageException("patch set " + ps + " not found");
       }
       d.writtenOn = ctx.getWhen();
       d.tag = tag;
@@ -69,7 +68,7 @@
       try {
         CommentsUtil.setCommentRevId(d, patchListCache, notes.getChange(), ps);
       } catch (PatchListNotAvailableException e) {
-        throw new OrmException(e);
+        throw new StorageException(e);
       }
     }
     commentsUtil.putComments(ctx.getUpdate(psId), PUBLISHED, drafts);
diff --git a/java/com/google/gerrit/server/StarredChangesUtil.java b/java/com/google/gerrit/server/StarredChangesUtil.java
index e6d6be1..ad3bfd6 100644
--- a/java/com/google/gerrit/server/StarredChangesUtil.java
+++ b/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -31,6 +31,7 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.common.primitives.Ints;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
@@ -46,7 +47,6 @@
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -186,12 +186,11 @@
     this.queryProvider = queryProvider;
   }
 
-  public ImmutableSortedSet<String> getLabels(Account.Id accountId, Change.Id changeId)
-      throws OrmException {
+  public ImmutableSortedSet<String> getLabels(Account.Id accountId, Change.Id changeId) {
     try (Repository repo = repoManager.openRepository(allUsers)) {
       return readLabels(repo, RefNames.refsStarredChanges(changeId, accountId)).labels();
     } catch (IOException e) {
-      throw new OrmException(
+      throw new StorageException(
           String.format(
               "Reading stars from change %d for account %d failed",
               changeId.get(), accountId.get()),
@@ -205,7 +204,7 @@
       Change.Id changeId,
       Set<String> labelsToAdd,
       Set<String> labelsToRemove)
-      throws OrmException, IllegalLabelException {
+      throws IllegalLabelException {
     try (Repository repo = repoManager.openRepository(allUsers)) {
       String refName = RefNames.refsStarredChanges(changeId, accountId);
       StarRef old = readLabels(repo, refName);
@@ -228,13 +227,13 @@
       indexer.index(project, changeId);
       return ImmutableSortedSet.copyOf(labels);
     } catch (IOException e) {
-      throw new OrmException(
+      throw new StorageException(
           String.format("Star change %d for account %d failed", changeId.get(), accountId.get()),
           e);
     }
   }
 
-  public void unstarAll(Project.NameKey project, Change.Id changeId) throws OrmException {
+  public void unstarAll(Project.NameKey project, Change.Id changeId) {
     try (Repository repo = repoManager.openRepository(allUsers);
         RevWalk rw = new RevWalk(repo)) {
       BatchRefUpdate batchUpdate = repo.getRefDatabase().newBatchUpdate();
@@ -257,11 +256,11 @@
       }
       indexer.index(project, changeId);
     } catch (IOException e) {
-      throw new OrmException(String.format("Unstar change %d failed", changeId.get()), e);
+      throw new StorageException(String.format("Unstar change %d failed", changeId.get()), e);
     }
   }
 
-  public ImmutableMap<Account.Id, StarRef> byChange(Change.Id changeId) throws OrmException {
+  public ImmutableMap<Account.Id, StarRef> byChange(Change.Id changeId) {
     try (Repository repo = repoManager.openRepository(allUsers)) {
       ImmutableMap.Builder<Account.Id, StarRef> builder = ImmutableMap.builder();
       for (String refPart : getRefNames(repo, RefNames.refsStarredChangesPrefix(changeId))) {
@@ -274,13 +273,12 @@
       }
       return builder.build();
     } catch (IOException e) {
-      throw new OrmException(
+      throw new StorageException(
           String.format("Get accounts that starred change %d failed", changeId.get()), e);
     }
   }
 
-  public ImmutableListMultimap<Account.Id, String> byChangeFromIndex(Change.Id changeId)
-      throws OrmException {
+  public ImmutableListMultimap<Account.Id, String> byChangeFromIndex(Change.Id changeId) {
     List<ChangeData> changeData =
         queryProvider
             .get()
@@ -311,7 +309,7 @@
     }
   }
 
-  public void ignore(ChangeResource rsrc) throws OrmException, IllegalLabelException {
+  public void ignore(ChangeResource rsrc) throws IllegalLabelException {
     star(
         rsrc.getUser().asIdentifiedUser().getAccountId(),
         rsrc.getProject(),
@@ -320,7 +318,7 @@
         ImmutableSet.of());
   }
 
-  public void unignore(ChangeResource rsrc) throws OrmException, IllegalLabelException {
+  public void unignore(ChangeResource rsrc) throws IllegalLabelException {
     star(
         rsrc.getUser().asIdentifiedUser().getAccountId(),
         rsrc.getProject(),
@@ -329,11 +327,11 @@
         ImmutableSet.of(IGNORE_LABEL));
   }
 
-  public boolean isIgnoredBy(Change.Id changeId, Account.Id accountId) throws OrmException {
+  public boolean isIgnoredBy(Change.Id changeId, Account.Id accountId) {
     return getLabels(accountId, changeId).contains(IGNORE_LABEL);
   }
 
-  public boolean isIgnored(ChangeResource rsrc) throws OrmException {
+  public boolean isIgnored(ChangeResource rsrc) {
     return isIgnoredBy(rsrc.getChange().getId(), rsrc.getUser().asIdentifiedUser().getAccountId());
   }
 
@@ -353,7 +351,7 @@
     return UNREVIEWED_LABEL + "/" + ps;
   }
 
-  public void markAsReviewed(ChangeResource rsrc) throws OrmException, IllegalLabelException {
+  public void markAsReviewed(ChangeResource rsrc) throws IllegalLabelException {
     star(
         rsrc.getUser().asIdentifiedUser().getAccountId(),
         rsrc.getProject(),
@@ -362,7 +360,7 @@
         ImmutableSet.of(getUnreviewedLabel(rsrc.getChange())));
   }
 
-  public void markAsUnreviewed(ChangeResource rsrc) throws OrmException, IllegalLabelException {
+  public void markAsUnreviewed(ChangeResource rsrc) throws IllegalLabelException {
     star(
         rsrc.getUser().asIdentifiedUser().getAccountId(),
         rsrc.getProject(),
@@ -444,7 +442,7 @@
 
   private void updateLabels(
       Repository repo, String refName, ObjectId oldObjectId, Collection<String> labels)
-      throws IOException, OrmException, InvalidLabelsException {
+      throws IOException, InvalidLabelsException {
     try (TraceTimer traceTimer =
             TraceContext.newTimer("Update star labels in %s (labels=%s)", refName, labels);
         RevWalk rw = new RevWalk(repo)) {
@@ -471,14 +469,13 @@
         case REJECTED_MISSING_OBJECT:
         case REJECTED_OTHER_REASON:
         default:
-          throw new OrmException(
+          throw new StorageException(
               String.format("Update star labels on ref %s failed: %s", refName, result.name()));
       }
     }
   }
 
-  private void deleteRef(Repository repo, String refName, ObjectId oldObjectId)
-      throws IOException, OrmException {
+  private void deleteRef(Repository repo, String refName, ObjectId oldObjectId) throws IOException {
     if (ObjectId.zeroId().equals(oldObjectId)) {
       // ref doesn't exist
       return;
@@ -507,7 +504,7 @@
         case REJECTED_MISSING_OBJECT:
         case REJECTED_OTHER_REASON:
         default:
-          throw new OrmException(
+          throw new StorageException(
               String.format("Delete star ref %s failed: %s", refName, result.name()));
       }
     }
diff --git a/java/com/google/gerrit/server/account/AccountConfig.java b/java/com/google/gerrit/server/account/AccountConfig.java
index d58036d..06f7a08 100644
--- a/java/com/google/gerrit/server/account/AccountConfig.java
+++ b/java/com/google/gerrit/server/account/AccountConfig.java
@@ -21,6 +21,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.exceptions.DuplicateKeyException;
 import com.google.gerrit.extensions.client.DiffPreferencesInfo;
 import com.google.gerrit.extensions.client.EditPreferencesInfo;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
@@ -34,7 +35,6 @@
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.git.meta.VersionedMetaData;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
 import java.io.IOException;
 import java.sql.Timestamp;
 import java.util.ArrayList;
@@ -200,9 +200,9 @@
    * Creates a new account.
    *
    * @return the new account
-   * @throws OrmDuplicateKeyException if the user branch already exists
+   * @throws DuplicateKeyException if the user branch already exists
    */
-  public Account getNewAccount() throws OrmDuplicateKeyException {
+  public Account getNewAccount() throws DuplicateKeyException {
     return getNewAccount(TimeUtil.nowTs());
   }
 
@@ -210,12 +210,12 @@
    * Creates a new account.
    *
    * @return the new account
-   * @throws OrmDuplicateKeyException if the user branch already exists
+   * @throws DuplicateKeyException if the user branch already exists
    */
-  Account getNewAccount(Timestamp registeredOn) throws OrmDuplicateKeyException {
+  Account getNewAccount(Timestamp registeredOn) throws DuplicateKeyException {
     checkLoaded();
     if (revision != null) {
-      throw new OrmDuplicateKeyException(String.format("account %s already exists", accountId));
+      throw new DuplicateKeyException(String.format("account %s already exists", accountId));
     }
     this.loadedAccountProperties =
         Optional.of(new AccountProperties(accountId, registeredOn, new Config(), null));
diff --git a/java/com/google/gerrit/server/account/AccountControl.java b/java/com/google/gerrit/server/account/AccountControl.java
index f17f387..4b8be81 100644
--- a/java/com/google/gerrit/server/account/AccountControl.java
+++ b/java/com/google/gerrit/server/account/AccountControl.java
@@ -17,7 +17,7 @@
 import static java.util.stream.Collectors.toSet;
 
 import com.google.gerrit.common.data.PermissionRule;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.AccountVisibility;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.Account;
diff --git a/java/com/google/gerrit/server/account/AccountManager.java b/java/com/google/gerrit/server/account/AccountManager.java
index d97ff7b..eb6b491 100644
--- a/java/com/google/gerrit/server/account/AccountManager.java
+++ b/java/com/google/gerrit/server/account/AccountManager.java
@@ -25,7 +25,8 @@
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.AccountFieldName;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -42,7 +43,6 @@
 import com.google.gerrit.server.notedb.Sequences;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.ssh.SshKeyCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -163,7 +163,7 @@
       // return the identity to the caller.
       update(who, extId);
       return new AuthResult(extId.accountId(), who.getExternalIdKey(), false);
-    } catch (OrmException | ConfigInvalidException e) {
+    } catch (StorageException | ConfigInvalidException e) {
       throw new AccountException("Authentication error", e);
     }
   }
@@ -214,7 +214,7 @@
   }
 
   private void update(AuthRequest who, ExternalId extId)
-      throws OrmException, IOException, ConfigInvalidException, AccountException {
+      throws IOException, ConfigInvalidException, AccountException {
     IdentifiedUser user = userFactory.create(extId.accountId());
     List<Consumer<InternalAccountUpdate.Builder>> accountUpdates = new ArrayList<>();
 
@@ -266,12 +266,12 @@
               user.getAccountId(),
               AccountUpdater.joinConsumers(accountUpdates))
           .orElseThrow(
-              () -> new OrmException("Account " + user.getAccountId() + " has been deleted"));
+              () -> new StorageException("Account " + user.getAccountId() + " has been deleted"));
     }
   }
 
   private AuthResult create(AuthRequest who)
-      throws OrmException, AccountException, IOException, ConfigInvalidException {
+      throws AccountException, IOException, ConfigInvalidException {
     Account.Id newId = new Account.Id(sequences.nextAccountId());
     logger.atFine().log("Assigning new Id %s to account", newId);
 
@@ -375,7 +375,7 @@
   }
 
   private void addGroupMember(AccountGroup.UUID groupUuid, IdentifiedUser user)
-      throws OrmException, IOException, ConfigInvalidException, AccountException {
+      throws IOException, ConfigInvalidException, AccountException {
     // The user initiated this request by logging in. -> Attribute all modifications to that user.
     GroupsUpdate groupsUpdate = groupsUpdateFactory.create(user);
     InternalGroupUpdate groupUpdate =
@@ -400,7 +400,7 @@
    *     this time.
    */
   public AuthResult link(Account.Id to, AuthRequest who)
-      throws AccountException, OrmException, IOException, ConfigInvalidException {
+      throws AccountException, IOException, ConfigInvalidException {
     Optional<ExternalId> optionalExtId = externalIds.get(who.getExternalIdKey());
     if (optionalExtId.isPresent()) {
       ExternalId extId = optionalExtId.get();
@@ -437,12 +437,11 @@
    * @param to account to link the identity onto.
    * @param who the additional identity.
    * @return the result of linking the identity to the user.
-   * @throws OrmException
    * @throws AccountException the identity belongs to a different account, or it cannot be linked at
    *     this time.
    */
   public AuthResult updateLink(Account.Id to, AuthRequest who)
-      throws OrmException, AccountException, IOException, ConfigInvalidException {
+      throws AccountException, IOException, ConfigInvalidException {
     accountsUpdateProvider
         .get()
         .update(
@@ -474,7 +473,7 @@
    *     found
    */
   public void unlink(Account.Id from, ExternalId.Key extIdKey)
-      throws AccountException, OrmException, IOException, ConfigInvalidException {
+      throws AccountException, IOException, ConfigInvalidException {
     unlink(from, ImmutableList.of(extIdKey));
   }
 
@@ -487,7 +486,7 @@
    *     identity was not found
    */
   public void unlink(Account.Id from, Collection<ExternalId.Key> extIdKeys)
-      throws AccountException, OrmException, IOException, ConfigInvalidException {
+      throws AccountException, IOException, ConfigInvalidException {
     if (extIdKeys.isEmpty()) {
       return;
     }
diff --git a/java/com/google/gerrit/server/account/AccountResolver.java b/java/com/google/gerrit/server/account/AccountResolver.java
index 14f4a38..2ac7147 100644
--- a/java/com/google/gerrit/server/account/AccountResolver.java
+++ b/java/com/google/gerrit/server/account/AccountResolver.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.config.AnonymousCowardName;
 import com.google.gerrit.server.query.account.InternalAccountQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -216,14 +215,14 @@
       return false;
     }
 
-    Optional<I> tryParse(String input) throws IOException, OrmException;
+    Optional<I> tryParse(String input) throws IOException;
 
-    Stream<AccountState> search(I input) throws OrmException, IOException, ConfigInvalidException;
+    Stream<AccountState> search(I input) throws IOException, ConfigInvalidException;
 
     boolean shortCircuitIfNoResults();
 
     default Optional<Stream<AccountState>> trySearch(String input)
-        throws OrmException, IOException, ConfigInvalidException {
+        throws IOException, ConfigInvalidException {
       Optional<I> parsed = tryParse(input);
       return parsed.isPresent() ? Optional.of(search(parsed.get())) : Optional.empty();
     }
@@ -335,7 +334,7 @@
     }
 
     @Override
-    public Stream<AccountState> search(String nameOrEmail) throws OrmException, IOException {
+    public Stream<AccountState> search(String nameOrEmail) throws IOException {
       // TODO(dborowitz): This would probably work as a Searcher<Address>
       int lt = nameOrEmail.indexOf('<');
       int gt = nameOrEmail.indexOf('>');
@@ -368,7 +367,7 @@
     }
 
     @Override
-    public Stream<AccountState> search(String input) throws OrmException, IOException {
+    public Stream<AccountState> search(String input) throws IOException {
       return toAccountStates(emails.getAccountFor(input));
     }
 
@@ -397,7 +396,7 @@
     }
 
     @Override
-    public Optional<AccountState> tryParse(String input) throws OrmException {
+    public Optional<AccountState> tryParse(String input) {
       List<AccountState> results =
           accountQueryProvider.get().enforceVisibility(true).byFullName(input);
       return results.size() == 1 ? Optional.of(results.get(0)) : Optional.empty();
@@ -426,7 +425,7 @@
     }
 
     @Override
-    public Stream<AccountState> search(String input) throws OrmException {
+    public Stream<AccountState> search(String input) {
       // At this point we have no clue. Just perform a whole bunch of suggestions and pray we come
       // up with a reasonable result list.
       // TODO(dborowitz): This doesn't match the documentation; consider whether it's possible to be
@@ -513,11 +512,10 @@
    *
    * @param input input string.
    * @return a result describing matching accounts. Never null even if the result set is empty.
-   * @throws OrmException if an error occurs.
    * @throws ConfigInvalidException if an error occurs.
    * @throws IOException if an error occurs.
    */
-  public Result resolve(String input) throws OrmException, ConfigInvalidException, IOException {
+  public Result resolve(String input) throws ConfigInvalidException, IOException {
     return searchImpl(input, searchers, visibilitySupplier());
   }
 
@@ -539,15 +537,13 @@
    *
    * @param input input string.
    * @return a result describing matching accounts. Never null even if the result set is empty.
-   * @throws OrmException if an error occurs.
    * @throws ConfigInvalidException if an error occurs.
    * @throws IOException if an error occurs.
    * @deprecated for use only by MailUtil for parsing commit footers; that class needs to be
    *     reevaluated.
    */
   @Deprecated
-  public Result resolveByNameOrEmail(String input)
-      throws OrmException, ConfigInvalidException, IOException {
+  public Result resolveByNameOrEmail(String input) throws ConfigInvalidException, IOException {
     return searchImpl(input, nameOrEmailSearchers, visibilitySupplier());
   }
 
@@ -560,7 +556,7 @@
       String input,
       ImmutableList<Searcher<?>> searchers,
       Supplier<Predicate<AccountState>> visibilitySupplier)
-      throws OrmException, ConfigInvalidException, IOException {
+      throws ConfigInvalidException, IOException {
     visibilitySupplier = Suppliers.memoize(visibilitySupplier::get);
     List<AccountState> inactive = new ArrayList<>();
 
diff --git a/java/com/google/gerrit/server/account/AccountsUpdate.java b/java/com/google/gerrit/server/account/AccountsUpdate.java
index b862793..4aac8ea 100644
--- a/java/com/google/gerrit/server/account/AccountsUpdate.java
+++ b/java/com/google/gerrit/server/account/AccountsUpdate.java
@@ -24,6 +24,8 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.Runnables;
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.git.LockFailureException;
 import com.google.gerrit.git.RefUpdateUtil;
 import com.google.gerrit.reviewdb.client.Account;
@@ -41,8 +43,6 @@
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryHelper.Action;
 import com.google.gerrit.server.update.RetryHelper.ActionType;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
@@ -293,14 +293,13 @@
    * @param accountId ID of the new account
    * @param init consumer to populate the new account
    * @return the newly created account
-   * @throws OrmDuplicateKeyException if the account already exists
+   * @throws DuplicateKeyException if the account already exists
    * @throws IOException if creating the user branch fails due to an IO error
-   * @throws OrmException if creating the user branch fails
    * @throws ConfigInvalidException if any of the account fields has an invalid value
    */
   public AccountState insert(
       String message, Account.Id accountId, Consumer<InternalAccountUpdate.Builder> init)
-      throws OrmException, IOException, ConfigInvalidException {
+      throws IOException, ConfigInvalidException {
     return insert(message, accountId, AccountUpdater.fromConsumer(init));
   }
 
@@ -311,13 +310,12 @@
    * @param accountId ID of the new account
    * @param updater updater to populate the new account
    * @return the newly created account
-   * @throws OrmDuplicateKeyException if the account already exists
+   * @throws DuplicateKeyException if the account already exists
    * @throws IOException if creating the user branch fails due to an IO error
-   * @throws OrmException if creating the user branch fails
    * @throws ConfigInvalidException if any of the account fields has an invalid value
    */
   public AccountState insert(String message, Account.Id accountId, AccountUpdater updater)
-      throws OrmException, IOException, ConfigInvalidException {
+      throws IOException, ConfigInvalidException {
     return updateAccount(
             r -> {
               AccountConfig accountConfig = read(r, accountId);
@@ -351,12 +349,11 @@
    * @throws IOException if updating the user branch fails due to an IO error
    * @throws LockFailureException if updating the user branch still fails due to concurrent updates
    *     after the retry timeout exceeded
-   * @throws OrmException if updating the user branch fails
    * @throws ConfigInvalidException if any of the account fields has an invalid value
    */
   public Optional<AccountState> update(
       String message, Account.Id accountId, Consumer<InternalAccountUpdate.Builder> update)
-      throws OrmException, LockFailureException, IOException, ConfigInvalidException {
+      throws LockFailureException, IOException, ConfigInvalidException {
     return update(message, accountId, AccountUpdater.fromConsumer(update));
   }
 
@@ -372,11 +369,10 @@
    * @throws IOException if updating the user branch fails due to an IO error
    * @throws LockFailureException if updating the user branch still fails due to concurrent updates
    *     after the retry timeout exceeded
-   * @throws OrmException if updating the user branch fails
    * @throws ConfigInvalidException if any of the account fields has an invalid value
    */
   public Optional<AccountState> update(String message, Account.Id accountId, AccountUpdater updater)
-      throws OrmException, LockFailureException, IOException, ConfigInvalidException {
+      throws LockFailureException, IOException, ConfigInvalidException {
     return updateAccount(
         r -> {
           AccountConfig accountConfig = read(r, accountId);
@@ -407,7 +403,7 @@
   }
 
   private Optional<AccountState> updateAccount(AccountUpdate accountUpdate)
-      throws IOException, ConfigInvalidException, OrmException {
+      throws IOException, ConfigInvalidException {
     return executeAccountUpdate(
         () -> {
           try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
@@ -423,7 +419,7 @@
   }
 
   private Optional<AccountState> executeAccountUpdate(Action<Optional<AccountState>> action)
-      throws IOException, ConfigInvalidException, OrmException {
+      throws IOException, ConfigInvalidException {
     try {
       return retryHelper.execute(
           ActionType.ACCOUNT_UPDATE, action, LockFailureException.class::isInstance);
@@ -431,8 +427,8 @@
       Throwables.throwIfUnchecked(e);
       Throwables.throwIfInstanceOf(e, IOException.class);
       Throwables.throwIfInstanceOf(e, ConfigInvalidException.class);
-      Throwables.throwIfInstanceOf(e, OrmException.class);
-      throw new OrmException(e);
+      Throwables.throwIfInstanceOf(e, StorageException.class);
+      throw new StorageException(e);
     }
   }
 
@@ -441,7 +437,7 @@
       Optional<ObjectId> rev,
       Account.Id accountId,
       InternalAccountUpdate update)
-      throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
+      throws IOException, ConfigInvalidException, DuplicateKeyException {
     ExternalIdNotes.checkSameAccount(
         Iterables.concat(
             update.getCreatedExternalIds(),
@@ -562,8 +558,7 @@
 
   @FunctionalInterface
   private static interface AccountUpdate {
-    UpdatedAccount update(Repository allUsersRepo)
-        throws IOException, ConfigInvalidException, OrmException;
+    UpdatedAccount update(Repository allUsersRepo) throws IOException, ConfigInvalidException;
   }
 
   private static class UpdatedAccount {
diff --git a/java/com/google/gerrit/server/account/DefaultRealm.java b/java/com/google/gerrit/server/account/DefaultRealm.java
index dde6e81..33de2d2 100644
--- a/java/com/google/gerrit/server/account/DefaultRealm.java
+++ b/java/com/google/gerrit/server/account/DefaultRealm.java
@@ -16,11 +16,11 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Strings;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.AccountFieldName;
 import com.google.gerrit.extensions.client.AuthType;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.config.AuthConfig;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -87,7 +87,7 @@
         if (1 == c.size()) {
           return c.iterator().next();
         }
-      } catch (OrmException e) {
+      } catch (StorageException e) {
         throw new IOException("Failed to query accounts by email", e);
       }
     }
diff --git a/java/com/google/gerrit/server/account/Emails.java b/java/com/google/gerrit/server/account/Emails.java
index 7c798ca..d1e2959 100644
--- a/java/com/google/gerrit/server/account/Emails.java
+++ b/java/com/google/gerrit/server/account/Emails.java
@@ -20,6 +20,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSetMultimap;
 import com.google.common.collect.Streams;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.account.externalids.ExternalIds;
@@ -27,7 +28,6 @@
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryHelper.Action;
 import com.google.gerrit.server.update.RetryHelper.ActionType;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -69,7 +69,7 @@
    *
    * @see #getAccountsFor(String...)
    */
-  public ImmutableSet<Account.Id> getAccountFor(String email) throws IOException, OrmException {
+  public ImmutableSet<Account.Id> getAccountFor(String email) throws IOException {
     return Streams.concat(
             externalIds.byEmail(email).stream().map(ExternalId::accountId),
             executeIndexQuery(() -> queryProvider.get().byPreferredEmail(email).stream())
@@ -83,7 +83,7 @@
    * @see #getAccountFor(String)
    */
   public ImmutableSetMultimap<String, Account.Id> getAccountsFor(String... emails)
-      throws IOException, OrmException {
+      throws IOException {
     ImmutableSetMultimap.Builder<String, Account.Id> builder = ImmutableSetMultimap.builder();
     externalIds.byEmails(emails).entries().stream()
         .forEach(e -> builder.put(e.getKey(), e.getValue().accountId()));
@@ -102,13 +102,14 @@
     return externalIds.byEmail(email).stream().map(ExternalId::accountId).collect(toImmutableSet());
   }
 
-  private <T> T executeIndexQuery(Action<T> action) throws OrmException {
+  private <T> T executeIndexQuery(Action<T> action) {
     try {
-      return retryHelper.execute(ActionType.INDEX_QUERY, action, OrmException.class::isInstance);
+      return retryHelper.execute(
+          ActionType.INDEX_QUERY, action, StorageException.class::isInstance);
     } catch (Exception e) {
       Throwables.throwIfUnchecked(e);
-      Throwables.throwIfInstanceOf(e, OrmException.class);
-      throw new OrmException(e);
+      Throwables.throwIfInstanceOf(e, StorageException.class);
+      throw new StorageException(e);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/account/GroupControl.java b/java/com/google/gerrit/server/account/GroupControl.java
index 5649629..b3e6739 100644
--- a/java/com/google/gerrit/server/account/GroupControl.java
+++ b/java/com/google/gerrit/server/account/GroupControl.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.account;
 
 import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
diff --git a/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java b/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
index a089d9e..c27d6c3 100644
--- a/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
+++ b/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
@@ -30,7 +30,6 @@
 import com.google.gerrit.server.logging.TraceContext;
 import com.google.gerrit.server.logging.TraceContext.TraceTimer;
 import com.google.gerrit.server.query.group.InternalGroupQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Module;
 import com.google.inject.Provider;
@@ -152,7 +151,7 @@
     }
 
     @Override
-    public ImmutableSet<AccountGroup.UUID> load(Account.Id memberId) throws OrmException {
+    public ImmutableSet<AccountGroup.UUID> load(Account.Id memberId) {
       try (TraceTimer timer = TraceContext.newTimer("Loading groups with member %s", memberId)) {
         return groupQueryProvider.get().byMember(memberId).stream()
             .map(InternalGroup::getGroupUUID)
@@ -171,7 +170,7 @@
     }
 
     @Override
-    public ImmutableList<AccountGroup.UUID> load(AccountGroup.UUID key) throws OrmException {
+    public ImmutableList<AccountGroup.UUID> load(AccountGroup.UUID key) {
       try (TraceTimer timer = TraceContext.newTimer("Loading parent groups of %s", key)) {
         return groupQueryProvider.get().bySubgroup(key).stream()
             .map(InternalGroup::getGroupUUID)
diff --git a/java/com/google/gerrit/server/account/SetInactiveFlag.java b/java/com/google/gerrit/server/account/SetInactiveFlag.java
index a683849..da2d640 100644
--- a/java/com/google/gerrit/server/account/SetInactiveFlag.java
+++ b/java/com/google/gerrit/server/account/SetInactiveFlag.java
@@ -23,7 +23,6 @@
 import com.google.gerrit.server.plugincontext.PluginSetContext;
 import com.google.gerrit.server.validators.AccountActivationValidationListener;
 import com.google.gerrit.server.validators.ValidationException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -48,7 +47,7 @@
   }
 
   public Response<?> deactivate(Account.Id accountId)
-      throws RestApiException, IOException, ConfigInvalidException, OrmException {
+      throws RestApiException, IOException, ConfigInvalidException {
     AtomicBoolean alreadyInactive = new AtomicBoolean(false);
     AtomicReference<Optional<RestApiException>> exception = new AtomicReference<>(Optional.empty());
     accountsUpdateProvider
@@ -81,7 +80,7 @@
   }
 
   public Response<String> activate(Account.Id accountId)
-      throws RestApiException, IOException, ConfigInvalidException, OrmException {
+      throws RestApiException, IOException, ConfigInvalidException {
     AtomicBoolean alreadyActive = new AtomicBoolean(false);
     AtomicReference<Optional<RestApiException>> exception = new AtomicReference<>(Optional.empty());
     accountsUpdateProvider
diff --git a/java/com/google/gerrit/server/account/VersionedAuthorizedKeys.java b/java/com/google/gerrit/server/account/VersionedAuthorizedKeys.java
index 965f1ba..c7808de 100644
--- a/java/com/google/gerrit/server/account/VersionedAuthorizedKeys.java
+++ b/java/com/google/gerrit/server/account/VersionedAuthorizedKeys.java
@@ -20,7 +20,7 @@
 
 import com.google.common.base.Strings;
 import com.google.common.collect.Ordering;
-import com.google.gerrit.common.errors.InvalidSshKeyException;
+import com.google.gerrit.exceptions.InvalidSshKeyException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.IdentifiedUser;
diff --git a/java/com/google/gerrit/server/account/externalids/DuplicateExternalIdKeyException.java b/java/com/google/gerrit/server/account/externalids/DuplicateExternalIdKeyException.java
index b4c82d0..aa09278 100644
--- a/java/com/google/gerrit/server/account/externalids/DuplicateExternalIdKeyException.java
+++ b/java/com/google/gerrit/server/account/externalids/DuplicateExternalIdKeyException.java
@@ -14,13 +14,13 @@
 
 package com.google.gerrit.server.account.externalids;
 
-import com.google.gwtorm.server.OrmDuplicateKeyException;
+import com.google.gerrit.exceptions.DuplicateKeyException;
 
 /**
  * Exception that is thrown if an external ID cannot be inserted because an external ID with the
  * same key already exists.
  */
-public class DuplicateExternalIdKeyException extends OrmDuplicateKeyException {
+public class DuplicateExternalIdKeyException extends DuplicateKeyException {
   private static final long serialVersionUID = 1L;
 
   private final ExternalId.Key duplicateKey;
diff --git a/java/com/google/gerrit/server/api/BUILD b/java/com/google/gerrit/server/api/BUILD
index d4f11f0..b9e26de 100644
--- a/java/com/google/gerrit/server/api/BUILD
+++ b/java/com/google/gerrit/server/api/BUILD
@@ -7,6 +7,7 @@
     deps = [
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/lifecycle",
         "//java/com/google/gerrit/reviewdb:server",
@@ -15,7 +16,6 @@
         "//java/com/google/gerrit/util/cli",
         "//lib:args4j",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:servlet-api-3_1",
         "//lib/guice",
         "//lib/guice:guice-assistedinject",
diff --git a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
index 6763f51..f688718 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ListMultimap;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.AbandonInput;
 import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.extensions.api.changes.AddReviewerResult;
@@ -103,7 +104,6 @@
 import com.google.gerrit.server.restapi.change.SuggestChangeReviewers;
 import com.google.gerrit.server.restapi.change.Unignore;
 import com.google.gerrit.util.cli.CmdLineParser;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.google.inject.Provider;
@@ -613,7 +613,7 @@
       } else {
         unignore.apply(change, new Input());
       }
-    } catch (OrmException | IllegalLabelException e) {
+    } catch (StorageException | IllegalLabelException e) {
       throw asRestApiException("Cannot ignore change", e);
     }
   }
@@ -622,7 +622,7 @@
   public boolean ignored() throws RestApiException {
     try {
       return stars.isIgnored(change);
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       throw asRestApiException("Cannot check if ignored", e);
     }
   }
@@ -637,7 +637,7 @@
       } else {
         markAsUnreviewed.apply(change, new Input());
       }
-    } catch (OrmException | IllegalLabelException e) {
+    } catch (StorageException | IllegalLabelException e) {
       throw asRestApiException(
           "Cannot mark change as " + (reviewed ? "reviewed" : "unreviewed"), e);
     }
diff --git a/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
index 5aa4cf1..ffc6524 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeEditApiImpl.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.server.restapi.change.DeleteChangeEdit;
 import com.google.gerrit.server.restapi.change.PublishChangeEdit;
 import com.google.gerrit.server.restapi.change.RebaseChangeEdit;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
@@ -242,7 +241,7 @@
   }
 
   private ChangeEditResource getChangeEditResource(String filePath)
-      throws ResourceNotFoundException, AuthException, IOException, OrmException {
+      throws ResourceNotFoundException, AuthException, IOException {
     return changeEdits.parse(changeResource, IdString.fromDecoded(filePath));
   }
 }
diff --git a/java/com/google/gerrit/server/api/projects/ProjectsImpl.java b/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
index 721d878..5d25d1a 100644
--- a/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
+++ b/java/com/google/gerrit/server/api/projects/ProjectsImpl.java
@@ -16,6 +16,7 @@
 
 import static com.google.gerrit.server.api.ApiUtil.asRestApiException;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.projects.ProjectApi;
 import com.google.gerrit.extensions.api.projects.ProjectInput;
 import com.google.gerrit.extensions.api.projects.Projects;
@@ -28,7 +29,6 @@
 import com.google.gerrit.server.restapi.project.ListProjects.FilterType;
 import com.google.gerrit.server.restapi.project.ProjectsCollection;
 import com.google.gerrit.server.restapi.project.QueryProjects;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -155,7 +155,7 @@
           .withLimit(r.getLimit())
           .withStart(r.getStart())
           .apply();
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       throw new RestApiException("Cannot query projects", e);
     }
   }
diff --git a/java/com/google/gerrit/server/args4j/AccountIdHandler.java b/java/com/google/gerrit/server/args4j/AccountIdHandler.java
index addb7f9..efc1866 100644
--- a/java/com/google/gerrit/server/args4j/AccountIdHandler.java
+++ b/java/com/google/gerrit/server/args4j/AccountIdHandler.java
@@ -16,6 +16,7 @@
 
 import static com.google.gerrit.util.cli.Localizable.localizable;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.AuthType;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Account;
@@ -25,7 +26,6 @@
 import com.google.gerrit.server.account.AuthRequest;
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.config.AuthConfig;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
@@ -81,7 +81,7 @@
             throw new CmdLineException(owner, localizable("user \"%s\" not found"), token);
         }
       }
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       throw new CmdLineException(owner, localizable("database is down"));
     } catch (IOException e) {
       throw new CmdLineException(owner, "Failed to load account", e);
diff --git a/java/com/google/gerrit/server/args4j/ChangeIdHandler.java b/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
index 13832fa..77f0fd8 100644
--- a/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
+++ b/java/com/google/gerrit/server/args4j/ChangeIdHandler.java
@@ -17,12 +17,12 @@
 import static com.google.gerrit.util.cli.Localizable.localizable;
 
 import com.google.common.base.Splitter;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
@@ -67,7 +67,7 @@
       }
     } catch (IllegalArgumentException e) {
       throw new CmdLineException(owner, localizable("Change-Id is not valid"));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       throw new CmdLineException(owner, localizable("Database error: %s"), e.getMessage());
     }
 
diff --git a/java/com/google/gerrit/server/audit/BUILD b/java/com/google/gerrit/server/audit/BUILD
index f66c129..fc8a3cc 100644
--- a/java/com/google/gerrit/server/audit/BUILD
+++ b/java/com/google/gerrit/server/audit/BUILD
@@ -55,7 +55,6 @@
         "//lib:gson",
         "//lib:guava",
         "//lib:guava-retrying",
-        "//lib:gwtorm",
         "//lib:jsch",
         "//lib:juniversalchardet",
         "//lib:mime-util",
diff --git a/java/com/google/gerrit/server/cache/serialize/BUILD b/java/com/google/gerrit/server/cache/serialize/BUILD
index cd9912c..9600074 100644
--- a/java/com/google/gerrit/server/cache/serialize/BUILD
+++ b/java/com/google/gerrit/server/cache/serialize/BUILD
@@ -5,8 +5,8 @@
     deps = [
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/proto",
+        "//java/com/google/gwtorm",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:protobuf",
         "//lib/jgit/org.eclipse.jgit:jgit",
     ],
diff --git a/java/com/google/gerrit/server/change/AbandonOp.java b/java/com/google/gerrit/server/change/AbandonOp.java
index 458a680..5ee5bc7 100644
--- a/java/com/google/gerrit/server/change/AbandonOp.java
+++ b/java/com/google/gerrit/server/change/AbandonOp.java
@@ -32,7 +32,6 @@
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.Context;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
@@ -79,7 +78,7 @@
   }
 
   @Override
-  public boolean updateChange(ChangeContext ctx) throws OrmException, ResourceConflictException {
+  public boolean updateChange(ChangeContext ctx) throws ResourceConflictException {
     change = ctx.getChange();
     PatchSet.Id psId = change.currentPatchSetId();
     ChangeUpdate update = ctx.getUpdate(psId);
@@ -108,7 +107,7 @@
   }
 
   @Override
-  public void postUpdate(Context ctx) throws OrmException {
+  public void postUpdate(Context ctx) {
     NotifyResolver.Result notify = ctx.getNotify(change.getId());
     try {
       ReplyToChangeSender cm = abandonedSenderFactory.create(ctx.getProject(), change.getId());
diff --git a/java/com/google/gerrit/server/change/AbandonUtil.java b/java/com/google/gerrit/server/change/AbandonUtil.java
index f505f6d..6f46498 100644
--- a/java/com/google/gerrit/server/change/AbandonUtil.java
+++ b/java/com/google/gerrit/server/change/AbandonUtil.java
@@ -17,6 +17,7 @@
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ListMultimap;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.InternalUser;
@@ -25,7 +26,6 @@
 import com.google.gerrit.server.query.change.ChangeQueryBuilder;
 import com.google.gerrit.server.query.change.ChangeQueryProcessor;
 import com.google.gerrit.server.update.BatchUpdate;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -96,14 +96,14 @@
         }
       }
       logger.atInfo().log("Auto-Abandoned %d of %d changes.", count, changesToAbandon.size());
-    } catch (QueryParseException | OrmException e) {
+    } catch (QueryParseException | StorageException e) {
       logger.atSevere().withCause(e).log(
           "Failed to query inactive open changes for auto-abandoning.");
     }
   }
 
   private Collection<ChangeData> getValidChanges(Collection<ChangeData> changes, String query)
-      throws OrmException, QueryParseException {
+      throws QueryParseException {
     Collection<ChangeData> validChanges = new ArrayList<>();
     for (ChangeData cd : changes) {
       String newQuery = query + " change:" + cd.getId();
diff --git a/java/com/google/gerrit/server/change/AccountPatchReviewStore.java b/java/com/google/gerrit/server/change/AccountPatchReviewStore.java
index 69825ea..fc3e476 100644
--- a/java/com/google/gerrit/server/change/AccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/change/AccountPatchReviewStore.java
@@ -18,7 +18,6 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwtorm.server.OrmException;
 import java.util.Collection;
 import java.util.Optional;
 
@@ -54,9 +53,8 @@
    * @param path file path
    * @return {@code true} if the reviewed flag was updated, {@code false} if the reviewed flag was
    *     already set
-   * @throws OrmException thrown if updating the reviewed flag failed
    */
-  boolean markReviewed(PatchSet.Id psId, Account.Id accountId, String path) throws OrmException;
+  boolean markReviewed(PatchSet.Id psId, Account.Id accountId, String path);
 
   /**
    * Marks the given files in the given patch set as reviewed by the given user.
@@ -64,10 +62,8 @@
    * @param psId patch set ID
    * @param accountId account ID of the user
    * @param paths file paths
-   * @throws OrmException thrown if updating the reviewed flag failed
    */
-  void markReviewed(PatchSet.Id psId, Account.Id accountId, Collection<String> paths)
-      throws OrmException;
+  void markReviewed(PatchSet.Id psId, Account.Id accountId, Collection<String> paths);
 
   /**
    * Clears the reviewed flag for the given file in the given patch set for the given user.
@@ -75,17 +71,15 @@
    * @param psId patch set ID
    * @param accountId account ID of the user
    * @param path file path
-   * @throws OrmException thrown if clearing the reviewed flag failed
    */
-  void clearReviewed(PatchSet.Id psId, Account.Id accountId, String path) throws OrmException;
+  void clearReviewed(PatchSet.Id psId, Account.Id accountId, String path);
 
   /**
    * Clears the reviewed flags for all files in the given patch set for all users.
    *
    * @param psId patch set ID
-   * @throws OrmException thrown if clearing the reviewed flags failed
    */
-  void clearReviewed(PatchSet.Id psId) throws OrmException;
+  void clearReviewed(PatchSet.Id psId);
 
   /**
    * Find the latest patch set, that is smaller or equals to the given patch set, where at least,
@@ -95,8 +89,6 @@
    * @param accountId account ID of the user
    * @return optionally, all files the have been reviewed by the given user that belong to the patch
    *     set that is smaller or equals to the given patch set
-   * @throws OrmException thrown if accessing the reviewed flags failed
    */
-  Optional<PatchSetWithReviewedFiles> findReviewed(PatchSet.Id psId, Account.Id accountId)
-      throws OrmException;
+  Optional<PatchSetWithReviewedFiles> findReviewed(PatchSet.Id psId, Account.Id accountId);
 }
diff --git a/java/com/google/gerrit/server/change/ActionJson.java b/java/com/google/gerrit/server/change/ActionJson.java
index 52c0451..d493b31 100644
--- a/java/com/google/gerrit/server/change/ActionJson.java
+++ b/java/com/google/gerrit/server/change/ActionJson.java
@@ -32,7 +32,6 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.extensions.webui.UiActions;
 import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -70,7 +69,7 @@
     this.userProvider = userProvider;
   }
 
-  public Map<String, ActionInfo> format(RevisionResource rsrc) throws OrmException {
+  public Map<String, ActionInfo> format(RevisionResource rsrc) {
     ChangeInfo changeInfo = null;
     RevisionInfo revisionInfo = null;
     List<ActionVisitor> visitors = visitors();
@@ -97,7 +96,7 @@
   }
 
   public RevisionInfo addRevisionActions(
-      @Nullable ChangeInfo changeInfo, RevisionInfo to, RevisionResource rsrc) throws OrmException {
+      @Nullable ChangeInfo changeInfo, RevisionInfo to, RevisionResource rsrc) {
     List<ActionVisitor> visitors = visitors();
     if (!visitors.isEmpty()) {
       if (changeInfo != null) {
diff --git a/java/com/google/gerrit/server/change/AddReviewersOp.java b/java/com/google/gerrit/server/change/AddReviewersOp.java
index 46d57b5..610290d 100644
--- a/java/com/google/gerrit/server/change/AddReviewersOp.java
+++ b/java/com/google/gerrit/server/change/AddReviewersOp.java
@@ -44,7 +44,6 @@
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.Context;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
@@ -157,8 +156,7 @@
   }
 
   @Override
-  public boolean updateChange(ChangeContext ctx)
-      throws RestApiException, OrmException, IOException {
+  public boolean updateChange(ChangeContext ctx) throws RestApiException, IOException {
     change = ctx.getChange();
     if (!accountIds.isEmpty()) {
       if (state == CC) {
diff --git a/java/com/google/gerrit/server/change/ChangeFinder.java b/java/com/google/gerrit/server/change/ChangeFinder.java
index 5e7a9bf..4ad3c67 100644
--- a/java/com/google/gerrit/server/change/ChangeFinder.java
+++ b/java/com/google/gerrit/server/change/ChangeFinder.java
@@ -20,6 +20,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import com.google.common.primitives.Ints;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.restapi.DeprecatedIdentifierException;
 import com.google.gerrit.extensions.restapi.Url;
 import com.google.gerrit.index.IndexConfig;
@@ -37,7 +38,6 @@
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Module;
 import com.google.inject.Provider;
@@ -106,7 +106,7 @@
     this.allowedIdTypes = ImmutableSet.copyOf(configuredChangeIdTypes);
   }
 
-  public ChangeNotes findOne(String id) throws OrmException {
+  public ChangeNotes findOne(String id) {
     List<ChangeNotes> ctls = find(id);
     if (ctls.size() != 1) {
       return null;
@@ -119,14 +119,13 @@
    *
    * @param id change identifier.
    * @return possibly-empty list of notes for all matching changes; may or may not be visible.
-   * @throws OrmException if an error occurred querying the database.
    */
-  public List<ChangeNotes> find(String id) throws OrmException {
+  public List<ChangeNotes> find(String id) {
     try {
       return find(id, false);
     } catch (DeprecatedIdentifierException e) {
       // This can't happen because we don't enforce deprecation
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
   }
 
@@ -137,11 +136,10 @@
    * @param enforceDeprecation boolean to see if we should throw {@link
    *     DeprecatedIdentifierException} in case the identifier is deprecated
    * @return possibly-empty list of notes for all matching changes; may or may not be visible.
-   * @throws OrmException if an error occurred querying the database
    * @throws DeprecatedIdentifierException if the identifier is deprecated.
    */
   public List<ChangeNotes> find(String id, boolean enforceDeprecation)
-      throws OrmException, DeprecatedIdentifierException {
+      throws DeprecatedIdentifierException {
     if (id.isEmpty()) {
       return Collections.emptyList();
     }
@@ -194,17 +192,16 @@
     return notes;
   }
 
-  private List<ChangeNotes> fromProjectNumber(String project, int changeNumber)
-      throws OrmException {
+  private List<ChangeNotes> fromProjectNumber(String project, int changeNumber) {
     Change.Id cId = new Change.Id(changeNumber);
     try {
       return ImmutableList.of(
           changeNotesFactory.createChecked(Project.NameKey.parse(project), cId));
     } catch (NoSuchChangeException e) {
       return Collections.emptyList();
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       // Distinguish between a RepositoryNotFoundException (project argument invalid) and
-      // other OrmExceptions (failure in the persistence layer).
+      // other StorageExceptions (failure in the persistence layer).
       if (Throwables.getRootCause(e) instanceof RepositoryNotFoundException) {
         return Collections.emptyList();
       }
@@ -212,7 +209,7 @@
     }
   }
 
-  public ChangeNotes findOne(Change.Id id) throws OrmException {
+  public ChangeNotes findOne(Change.Id id) {
     List<ChangeNotes> notes = find(id);
     if (notes.size() != 1) {
       throw new NoSuchChangeException(id);
@@ -220,7 +217,7 @@
     return notes.get(0);
   }
 
-  public List<ChangeNotes> find(Change.Id id) throws OrmException {
+  public List<ChangeNotes> find(Change.Id id) {
     String project = changeIdProjectCache.getIfPresent(id);
     if (project != null) {
       return fromProjectNumber(project, id.get());
@@ -236,7 +233,7 @@
     return asChangeNotes(r);
   }
 
-  private List<ChangeNotes> asChangeNotes(List<ChangeData> cds) throws OrmException {
+  private List<ChangeNotes> asChangeNotes(List<ChangeData> cds) {
     List<ChangeNotes> notes = new ArrayList<>(cds.size());
     if (!indexConfig.separateChangeSubIndexes()) {
       for (ChangeData cd : cds) {
diff --git a/java/com/google/gerrit/server/change/ChangeInserter.java b/java/com/google/gerrit/server/change/ChangeInserter.java
index 5371908..9f9ec1f 100644
--- a/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -68,7 +68,6 @@
 import com.google.gerrit.server.update.InsertChangeOp;
 import com.google.gerrit.server.update.RepoContext;
 import com.google.gerrit.server.util.RequestScopePropagator;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
@@ -369,8 +368,7 @@
 
   @Override
   public boolean updateChange(ChangeContext ctx)
-      throws RestApiException, OrmException, IOException, PermissionBackendException,
-          ConfigInvalidException {
+      throws RestApiException, IOException, PermissionBackendException, ConfigInvalidException {
     change = ctx.getChange(); // Use defensive copy created by ChangeControl.
     patchSetInfo =
         patchSetInfoFactory.get(ctx.getRevWalk(), ctx.getRevWalk().parseCommit(commitId), psId);
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index fdb51c4..9e43cee 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -48,6 +48,7 @@
 import com.google.gerrit.common.data.SubmitRecord.Status;
 import com.google.gerrit.common.data.SubmitRequirement;
 import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.FixInput;
 import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.extensions.client.ReviewerState;
@@ -96,7 +97,6 @@
 import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeData.ChangedLines;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -268,23 +268,23 @@
     return this;
   }
 
-  public ChangeInfo format(ChangeResource rsrc) throws OrmException {
+  public ChangeInfo format(ChangeResource rsrc) {
     return format(changeDataFactory.create(rsrc.getNotes()));
   }
 
-  public ChangeInfo format(Change change) throws OrmException {
+  public ChangeInfo format(Change change) {
     return format(changeDataFactory.create(change));
   }
 
-  public ChangeInfo format(Project.NameKey project, Change.Id id) throws OrmException {
+  public ChangeInfo format(Project.NameKey project, Change.Id id) {
     return format(project, id, ChangeInfo::new);
   }
 
-  public ChangeInfo format(ChangeData cd) throws OrmException {
+  public ChangeInfo format(ChangeData cd) {
     return format(cd, Optional.empty(), true, ChangeInfo::new);
   }
 
-  public ChangeInfo format(RevisionResource rsrc) throws OrmException {
+  public ChangeInfo format(RevisionResource rsrc) {
     ChangeData cd = changeDataFactory.create(rsrc.getNotes());
     return format(cd, Optional.of(rsrc.getPatchSet().getId()), true, ChangeInfo::new);
   }
@@ -308,8 +308,7 @@
     }
   }
 
-  public List<ChangeInfo> format(Collection<ChangeData> in)
-      throws OrmException, PermissionBackendException {
+  public List<ChangeInfo> format(Collection<ChangeData> in) throws PermissionBackendException {
     accountLoader = accountLoaderFactory.create(has(DETAILED_ACCOUNTS));
     ensureLoaded(in);
     List<ChangeInfo> out = new ArrayList<>(in.size());
@@ -321,11 +320,11 @@
   }
 
   public <I extends ChangeInfo> I format(
-      Project.NameKey project, Change.Id id, Supplier<I> changeInfoSupplier) throws OrmException {
+      Project.NameKey project, Change.Id id, Supplier<I> changeInfoSupplier) {
     ChangeNotes notes;
     try {
       notes = notesFactory.createChecked(project, id);
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       if (!has(CHECK)) {
         throw e;
       }
@@ -366,8 +365,7 @@
       ChangeData cd,
       Optional<PatchSet.Id> limitToPsId,
       boolean fillAccountLoader,
-      Supplier<I> changeInfoSupplier)
-      throws OrmException {
+      Supplier<I> changeInfoSupplier) {
     try {
       if (fillAccountLoader) {
         accountLoader = accountLoaderFactory.create(has(DETAILED_ACCOUNTS));
@@ -378,19 +376,18 @@
       return toChangeInfo(cd, limitToPsId, changeInfoSupplier);
     } catch (PatchListNotAvailableException
         | GpgException
-        | OrmException
         | IOException
         | PermissionBackendException
         | RuntimeException e) {
       if (!has(CHECK)) {
-        Throwables.throwIfInstanceOf(e, OrmException.class);
-        throw new OrmException(e);
+        Throwables.throwIfInstanceOf(e, StorageException.class);
+        throw new StorageException(e);
       }
       return checkOnly(cd, changeInfoSupplier);
     }
   }
 
-  private void ensureLoaded(Iterable<ChangeData> all) throws OrmException {
+  private void ensureLoaded(Iterable<ChangeData> all) {
     if (lazyLoad) {
       ChangeData.ensureChangeLoaded(all);
       if (has(ALL_REVISIONS)) {
@@ -425,7 +422,7 @@
         try {
           ensureLoaded(Collections.singleton(cd));
           changeInfos.add(format(cd, Optional.empty(), false, ChangeInfo::new));
-        } catch (OrmException | RuntimeException e) {
+        } catch (RuntimeException e) {
           logger.atWarning().withCause(e).log(
               "Omitting corrupt change %s from results", cd.getId());
         }
@@ -438,7 +435,7 @@
     ChangeNotes notes;
     try {
       notes = cd.notes();
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       String msg = "Error loading change";
       logger.atWarning().withCause(e).log(msg + " %s", cd.getId());
       I info = changeInfoSupplier.get();
@@ -477,8 +474,7 @@
 
   private <I extends ChangeInfo> I toChangeInfo(
       ChangeData cd, Optional<PatchSet.Id> limitToPsId, Supplier<I> changeInfoSupplier)
-      throws PatchListNotAvailableException, GpgException, OrmException, PermissionBackendException,
-          IOException {
+      throws PatchListNotAvailableException, GpgException, PermissionBackendException, IOException {
     try (Timer0.Context ignored = metrics.toChangeInfoLatency.start()) {
       return toChangeInfoImpl(cd, limitToPsId, changeInfoSupplier);
     }
@@ -486,8 +482,7 @@
 
   private <I extends ChangeInfo> I toChangeInfoImpl(
       ChangeData cd, Optional<PatchSet.Id> limitToPsId, Supplier<I> changeInfoSupplier)
-      throws PatchListNotAvailableException, GpgException, OrmException, PermissionBackendException,
-          IOException {
+      throws PatchListNotAvailableException, GpgException, PermissionBackendException, IOException {
     I out = changeInfoSupplier.get();
     CurrentUser user = userProvider.get();
 
@@ -638,7 +633,7 @@
     return reviewerMap;
   }
 
-  private Collection<ReviewerUpdateInfo> reviewerUpdates(ChangeData cd) throws OrmException {
+  private Collection<ReviewerUpdateInfo> reviewerUpdates(ChangeData cd) {
     List<ReviewerStatusUpdate> reviewerUpdates = cd.reviewerUpdates();
     List<ReviewerUpdateInfo> result = new ArrayList<>(reviewerUpdates.size());
     for (ReviewerStatusUpdate c : reviewerUpdates) {
@@ -656,7 +651,7 @@
     return SubmitRecord.allRecordsOK(cd.submitRecords(SUBMIT_RULE_OPTIONS_STRICT));
   }
 
-  private void setSubmitter(ChangeData cd, ChangeInfo out) throws OrmException {
+  private void setSubmitter(ChangeData cd, ChangeInfo out) {
     Optional<PatchSetApproval> s = cd.getSubmitApproval();
     if (!s.isPresent()) {
       return;
@@ -665,7 +660,7 @@
     out.submitter = accountLoader.get(s.get().getAccountId());
   }
 
-  private Collection<ChangeMessageInfo> messages(ChangeData cd) throws OrmException {
+  private Collection<ChangeMessageInfo> messages(ChangeData cd) {
     List<ChangeMessage> messages = cmUtil.byChange(cd.notes());
     if (messages.isEmpty()) {
       return Collections.emptyList();
@@ -679,7 +674,7 @@
   }
 
   private Collection<AccountInfo> removableReviewers(ChangeData cd, ChangeInfo out)
-      throws PermissionBackendException, OrmException {
+      throws PermissionBackendException {
     // Although this is called removableReviewers, this method also determines
     // which CCs are removable.
     //
@@ -767,8 +762,8 @@
         .collect(toList());
   }
 
-  private Map<PatchSet.Id, PatchSet> loadPatchSets(ChangeData cd, Optional<PatchSet.Id> limitToPsId)
-      throws OrmException {
+  private Map<PatchSet.Id, PatchSet> loadPatchSets(
+      ChangeData cd, Optional<PatchSet.Id> limitToPsId) {
     Collection<PatchSet> src;
     if (has(ALL_REVISIONS) || has(MESSAGES)) {
       src = cd.patchSets();
@@ -777,12 +772,12 @@
       if (limitToPsId.isPresent()) {
         ps = cd.patchSet(limitToPsId.get());
         if (ps == null) {
-          throw new OrmException("missing patch set " + limitToPsId.get());
+          throw new StorageException("missing patch set " + limitToPsId.get());
         }
       } else {
         ps = cd.currentPatchSet();
         if (ps == null) {
-          throw new OrmException("missing current patch set for change " + cd.getId());
+          throw new StorageException("missing current patch set for change " + cd.getId());
         }
       }
       src = Collections.singletonList(ps);
@@ -799,8 +794,7 @@
    *     from either an index-backed or a database-backed {@link ChangeData} depending on {@code
    *     lazyload}.
    */
-  private PermissionBackend.ForChange permissionBackendForChange(CurrentUser user, ChangeData cd)
-      throws OrmException {
+  private PermissionBackend.ForChange permissionBackendForChange(CurrentUser user, ChangeData cd) {
     PermissionBackend.WithUser withUser = permissionBackend.user(user);
     return lazyLoad
         ? withUser.change(cd)
diff --git a/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java b/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
index 170223d..4c68f2d 100644
--- a/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
+++ b/java/com/google/gerrit/server/change/ChangeKindCacheImpl.java
@@ -23,6 +23,7 @@
 import com.google.common.collect.FluentIterable;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.ChangeKind;
 import com.google.gerrit.proto.Protos;
 import com.google.gerrit.reviewdb.client.Change;
@@ -38,7 +39,6 @@
 import com.google.gerrit.server.git.InMemoryInserter;
 import com.google.gerrit.server.git.MergeUtil;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Module;
 import com.google.inject.name.Named;
@@ -392,7 +392,7 @@
                   ObjectId.fromString(priorPs.getRevision().get()),
                   ObjectId.fromString(patch.getRevision().get()));
         }
-      } catch (OrmException e) {
+      } catch (StorageException e) {
         // Do nothing; assume we have a complex change
         logger.atWarning().withCause(e).log(
             "Unable to get change kind for patchSet %s of change %s",
diff --git a/java/com/google/gerrit/server/change/ChangeResource.java b/java/com/google/gerrit/server/change/ChangeResource.java
index 3ec61ef..98b728f 100644
--- a/java/com/google/gerrit/server/change/ChangeResource.java
+++ b/java/com/google/gerrit/server/change/ChangeResource.java
@@ -21,6 +21,7 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.common.hash.Hasher;
 import com.google.common.hash.Hashing;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.restapi.RestResource;
 import com.google.gerrit.extensions.restapi.RestResource.HasETag;
 import com.google.gerrit.extensions.restapi.RestView;
@@ -39,7 +40,6 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.TypeLiteral;
 import com.google.inject.assistedinject.Assisted;
@@ -159,7 +159,7 @@
       // message is automatically added as reviewer. Hence if we include removed reviewers we can
       // be sure that we have all accounts that posted messages on the change.
       accounts.addAll(approvalUtil.getReviewers(notes).all());
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       // This ETag will be invalidated if it loads next time.
     }
 
@@ -175,7 +175,7 @@
     ObjectId noteId;
     try {
       noteId = notes.loadRevision();
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       noteId = null; // This ETag will be invalidated if it loads next time.
     }
     hashObjectId(h, noteId, buf);
diff --git a/java/com/google/gerrit/server/change/ConsistencyChecker.java b/java/com/google/gerrit/server/change/ConsistencyChecker.java
index 83c7387..f64bf1f 100644
--- a/java/com/google/gerrit/server/change/ConsistencyChecker.java
+++ b/java/com/google/gerrit/server/change/ConsistencyChecker.java
@@ -30,6 +30,7 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.FooterConstants;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.FixInput;
 import com.google.gerrit.extensions.common.ProblemInfo;
 import com.google.gerrit.extensions.common.ProblemInfo.Status;
@@ -55,7 +56,6 @@
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -232,7 +232,7 @@
         problem(
             String.format("Current patch set %d not found", change().currentPatchSetId().get()));
       }
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       error("Failed to look up current patch set", e);
     }
   }
@@ -256,7 +256,7 @@
     try {
       // Iterate in descending order.
       all = PS_ID_ORDER.sortedCopy(psUtil.byChange(notes));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       return error("Failed to look up patch sets", e);
     }
     patchSetsBySha = MultimapBuilder.hashKeys(all.size()).treeSetValues(PS_ID_ORDER).build();
@@ -425,7 +425,7 @@
           if (!c.getDest().equals(change().getDest())) {
             continue;
           }
-        } catch (OrmException e) {
+        } catch (StorageException e) {
           warn(e);
           // Include this patch set; should cause an error below, which is good.
         }
@@ -556,7 +556,7 @@
       notes = notesFactory.createChecked(inserter.getChange());
       insertPatchSetProblem.status = Status.FIXED;
       insertPatchSetProblem.outcome = "Inserted as patch set " + psId.get();
-    } catch (OrmException | IOException | UpdateException | RestApiException e) {
+    } catch (StorageException | IOException | UpdateException | RestApiException e) {
       warn(e);
       for (ProblemInfo pi : currProblems) {
         pi.status = Status.FIX_FAILED;
@@ -574,7 +574,7 @@
     }
 
     @Override
-    public boolean updateChange(ChangeContext ctx) throws OrmException {
+    public boolean updateChange(ChangeContext ctx) {
       ctx.getChange().setStatus(Change.Status.MERGED);
       ctx.getUpdate(ctx.getChange().currentPatchSetId()).fixStatus(Change.Status.MERGED);
       p.status = Status.FIXED;
@@ -672,10 +672,9 @@
     }
 
     @Override
-    public boolean updateChange(ChangeContext ctx)
-        throws OrmException, PatchSetInfoNotAvailableException {
+    public boolean updateChange(ChangeContext ctx) throws PatchSetInfoNotAvailableException {
       // Delete dangling key references.
-      accountPatchReviewStore.run(s -> s.clearReviewed(psId), OrmException.class);
+      accountPatchReviewStore.run(s -> s.clearReviewed(psId), StorageException.class);
 
       // For NoteDb setting the state to deleted is sufficient to filter everything out.
       ctx.getUpdate(psId).setPatchSetState(PatchSetState.DELETED);
@@ -706,7 +705,7 @@
 
     @Override
     public boolean updateChange(ChangeContext ctx)
-        throws OrmException, PatchSetInfoNotAvailableException, NoPatchSetsWouldRemainException {
+        throws PatchSetInfoNotAvailableException, NoPatchSetsWouldRemainException {
       if (!toDelete.contains(ctx.getChange().currentPatchSetId())) {
         return false;
       }
diff --git a/java/com/google/gerrit/server/change/DeleteChangeOp.java b/java/com/google/gerrit/server/change/DeleteChangeOp.java
index 1a579f3..966efea 100644
--- a/java/com/google/gerrit/server/change/DeleteChangeOp.java
+++ b/java/com/google/gerrit/server/change/DeleteChangeOp.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.change;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestApiException;
@@ -27,7 +28,6 @@
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.RepoContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
@@ -66,8 +66,7 @@
   // executed in a single atomic BatchRefUpdate. Actually deleting the change refs first would not
   // fail gracefully if the second delete fails, but fortunately that's not what happens.
   @Override
-  public boolean updateChange(ChangeContext ctx)
-      throws RestApiException, OrmException, IOException {
+  public boolean updateChange(ChangeContext ctx) throws RestApiException, IOException {
     Collection<PatchSet> patchSets = psUtil.byChange(ctx.getNotes());
 
     ensureDeletable(ctx, id, patchSets);
@@ -107,9 +106,9 @@
   }
 
   private void cleanUpReferences(ChangeContext ctx, Change.Id id, Collection<PatchSet> patchSets)
-      throws OrmException, NoSuchChangeException {
+      throws NoSuchChangeException {
     for (PatchSet ps : patchSets) {
-      accountPatchReviewStore.run(s -> s.clearReviewed(ps.getId()), OrmException.class);
+      accountPatchReviewStore.run(s -> s.clearReviewed(ps.getId()), StorageException.class);
     }
 
     // Non-atomic operation on Accounts table; not much we can do to make it
diff --git a/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java b/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java
index d7d4c0e..4b5572b 100644
--- a/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java
+++ b/java/com/google/gerrit/server/change/DeleteReviewerByEmailOp.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.Context;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Collections;
@@ -50,7 +49,7 @@
   }
 
   @Override
-  public boolean updateChange(ChangeContext ctx) throws OrmException {
+  public boolean updateChange(ChangeContext ctx) {
     change = ctx.getChange();
     PatchSet.Id psId = ctx.getChange().currentPatchSetId();
     String msg = "Removed reviewer " + reviewer;
diff --git a/java/com/google/gerrit/server/change/DeleteReviewerOp.java b/java/com/google/gerrit/server/change/DeleteReviewerOp.java
index 495ee84..29458a8 100644
--- a/java/com/google/gerrit/server/change/DeleteReviewerOp.java
+++ b/java/com/google/gerrit/server/change/DeleteReviewerOp.java
@@ -18,7 +18,7 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -43,7 +43,6 @@
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.Context;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
@@ -108,8 +107,7 @@
 
   @Override
   public boolean updateChange(ChangeContext ctx)
-      throws AuthException, ResourceNotFoundException, OrmException, PermissionBackendException,
-          IOException {
+      throws AuthException, ResourceNotFoundException, PermissionBackendException, IOException {
     Account.Id reviewerId = reviewer.getAccount().getId();
     // Check of removing this reviewer (even if there is no vote processed by the loop below) is OK
     removeReviewerControl.checkRemoveReviewer(ctx.getNotes(), ctx.getUser(), reviewerId);
@@ -194,8 +192,7 @@
         ctx.getWhen());
   }
 
-  private Iterable<PatchSetApproval> approvals(ChangeContext ctx, Account.Id accountId)
-      throws OrmException {
+  private Iterable<PatchSetApproval> approvals(ChangeContext ctx, Account.Id accountId) {
     Iterable<PatchSetApproval> approvals;
     approvals = approvalsUtil.byChange(ctx.getNotes()).values();
     return Iterables.filter(approvals, psa -> accountId.equals(psa.getAccountId()));
diff --git a/java/com/google/gerrit/server/change/LabelsJson.java b/java/com/google/gerrit/server/change/LabelsJson.java
index 01ac37b..6fde5a5 100644
--- a/java/com/google/gerrit/server/change/LabelsJson.java
+++ b/java/com/google/gerrit/server/change/LabelsJson.java
@@ -51,7 +51,6 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.sql.Timestamp;
@@ -100,7 +99,7 @@
    */
   Map<String, LabelInfo> labelsFor(
       AccountLoader accountLoader, ChangeData cd, boolean standard, boolean detailed)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     if (!standard && !detailed) {
       return null;
     }
@@ -115,7 +114,7 @@
 
   /** Returns all labels that the provided user has permission to vote on. */
   Map<String, Collection<String>> permittedLabels(Account.Id filterApprovalsBy, ChangeData cd)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     boolean isMerged = cd.change().isMerged();
     LabelTypes labelTypes = cd.getLabelTypes();
     Map<String, LabelType> toCheck = new HashMap<>();
@@ -194,7 +193,7 @@
       LabelTypes labelTypes,
       boolean standard,
       boolean detailed)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     Map<String, LabelWithStatus> labels = initLabels(accountLoader, cd, labelTypes, standard);
     if (detailed) {
       setAllApprovals(accountLoader, cd, labels);
@@ -252,8 +251,7 @@
     }
   }
 
-  private Map<String, Short> currentLabels(Account.Id accountId, ChangeData cd)
-      throws OrmException {
+  private Map<String, Short> currentLabels(Account.Id accountId, ChangeData cd) {
     Map<String, Short> result = new HashMap<>();
     for (PatchSetApproval psa :
         approvalsUtil.byPatchSetUser(
@@ -273,7 +271,7 @@
       LabelTypes labelTypes,
       boolean standard,
       boolean detailed)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     Set<Account.Id> allUsers = new HashSet<>();
     if (detailed) {
       // Users expect to see all reviewers on closed changes, even if they
@@ -431,7 +429,7 @@
 
   private void setAllApprovals(
       AccountLoader accountLoader, ChangeData cd, Map<String, LabelWithStatus> labels)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     checkState(
         !cd.change().isMerged(),
         "should not call setAllApprovals on %s change",
@@ -499,8 +497,7 @@
    *     from either an index-backed or a database-backed {@link ChangeData} depending on {@code
    *     lazyload}.
    */
-  private PermissionBackend.ForChange permissionBackendForChange(Account.Id user, ChangeData cd)
-      throws OrmException {
+  private PermissionBackend.ForChange permissionBackendForChange(Account.Id user, ChangeData cd) {
     PermissionBackend.WithUser withUser = permissionBackend.absentUser(user);
     return lazyLoad
         ? withUser.change(cd)
diff --git a/java/com/google/gerrit/server/change/NotifyResolver.java b/java/com/google/gerrit/server/change/NotifyResolver.java
index 65da083..724ef48 100644
--- a/java/com/google/gerrit/server/change/NotifyResolver.java
+++ b/java/com/google/gerrit/server/change/NotifyResolver.java
@@ -28,7 +28,6 @@
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.account.AccountResolver;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -81,7 +80,7 @@
 
   public Result resolve(
       NotifyHandling handling, @Nullable Map<RecipientType, NotifyInfo> notifyDetails)
-      throws BadRequestException, OrmException, IOException, ConfigInvalidException {
+      throws BadRequestException, IOException, ConfigInvalidException {
     requireNonNull(handling);
     ImmutableSetMultimap.Builder<RecipientType, Account.Id> b = ImmutableSetMultimap.builder();
     if (notifyDetails != null) {
@@ -93,7 +92,7 @@
   }
 
   private ImmutableList<Account.Id> find(@Nullable List<String> inputs)
-      throws OrmException, BadRequestException, IOException, ConfigInvalidException {
+      throws BadRequestException, IOException, ConfigInvalidException {
     if (inputs == null || inputs.isEmpty()) {
       return ImmutableList.of();
     }
diff --git a/java/com/google/gerrit/server/change/PatchSetInserter.java b/java/com/google/gerrit/server/change/PatchSetInserter.java
index 8f6b8f5..d3649f6 100644
--- a/java/com/google/gerrit/server/change/PatchSetInserter.java
+++ b/java/com/google/gerrit/server/change/PatchSetInserter.java
@@ -49,7 +49,6 @@
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.Context;
 import com.google.gerrit.server.update.RepoContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
@@ -187,15 +186,13 @@
 
   @Override
   public void updateRepo(RepoContext ctx)
-      throws AuthException, ResourceConflictException, IOException, OrmException,
-          PermissionBackendException {
+      throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
     validate(ctx);
     ctx.addRefUpdate(ObjectId.zeroId(), commitId, getPatchSetId().toRefName());
   }
 
   @Override
-  public boolean updateChange(ChangeContext ctx)
-      throws ResourceConflictException, OrmException, IOException {
+  public boolean updateChange(ChangeContext ctx) throws ResourceConflictException, IOException {
     change = ctx.getChange();
     ChangeUpdate update = ctx.getUpdate(psId);
     update.setSubjectForCommit("Create patch set " + psId.get());
@@ -271,8 +268,7 @@
   }
 
   private void validate(RepoContext ctx)
-      throws AuthException, ResourceConflictException, IOException, PermissionBackendException,
-          OrmException {
+      throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
     // Not allowed to create a new patch set if the current patch set is locked.
     psUtil.checkPatchSetNotLocked(origNotes);
 
diff --git a/java/com/google/gerrit/server/change/PureRevert.java b/java/com/google/gerrit/server/change/PureRevert.java
index e7fb67a..0859634 100644
--- a/java/com/google/gerrit/server/change/PureRevert.java
+++ b/java/com/google/gerrit/server/change/PureRevert.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.server.git.PureRevertCache;
 import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -38,7 +37,7 @@
   }
 
   public boolean get(ChangeNotes notes, Optional<String> claimedOriginal)
-      throws OrmException, IOException, BadRequestException, ResourceConflictException {
+      throws IOException, BadRequestException, ResourceConflictException {
     PatchSet currentPatchSet = notes.getCurrentPatchSet();
     if (currentPatchSet == null) {
       throw new ResourceConflictException("current revision is missing");
diff --git a/java/com/google/gerrit/server/change/RebaseChangeOp.java b/java/com/google/gerrit/server/change/RebaseChangeOp.java
index e4f4cc3..fccda7c 100644
--- a/java/com/google/gerrit/server/change/RebaseChangeOp.java
+++ b/java/com/google/gerrit/server/change/RebaseChangeOp.java
@@ -37,7 +37,6 @@
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.Context;
 import com.google.gerrit.server.update.RepoContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
@@ -149,7 +148,7 @@
   @Override
   public void updateRepo(RepoContext ctx)
       throws MergeConflictException, InvalidChangeOperationException, RestApiException, IOException,
-          OrmException, NoSuchChangeException, PermissionBackendException {
+          NoSuchChangeException, PermissionBackendException {
     // Ok that originalPatchSet was not read in a transaction, since we just
     // need its revision.
     RevId oldRev = originalPatchSet.getRevision();
@@ -213,15 +212,14 @@
   }
 
   @Override
-  public boolean updateChange(ChangeContext ctx)
-      throws ResourceConflictException, OrmException, IOException {
+  public boolean updateChange(ChangeContext ctx) throws ResourceConflictException, IOException {
     boolean ret = patchSetInserter.updateChange(ctx);
     rebasedPatchSet = patchSetInserter.getPatchSet();
     return ret;
   }
 
   @Override
-  public void postUpdate(Context ctx) throws OrmException {
+  public void postUpdate(Context ctx) {
     patchSetInserter.postUpdate(ctx);
   }
 
diff --git a/java/com/google/gerrit/server/change/RebaseUtil.java b/java/com/google/gerrit/server/change/RebaseUtil.java
index db68898..a4cf5ba 100644
--- a/java/com/google/gerrit/server/change/RebaseUtil.java
+++ b/java/com/google/gerrit/server/change/RebaseUtil.java
@@ -17,6 +17,7 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.flogger.FluentLogger;
 import com.google.common.primitives.Ints;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
@@ -28,7 +29,6 @@
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -62,7 +62,7 @@
       return true;
     } catch (RestApiException e) {
       return false;
-    } catch (OrmException | IOException e) {
+    } catch (StorageException | IOException e) {
       logger.atWarning().withCause(e).log(
           "Error checking if patch set %s on %s can be rebased", patchSet.getId(), dest);
       return false;
@@ -83,7 +83,7 @@
     public abstract PatchSet patchSet();
   }
 
-  public Base parseBase(RevisionResource rsrc, String base) throws OrmException {
+  public Base parseBase(RevisionResource rsrc, String base) {
     // Try parsing the base as a ref string.
     PatchSet.Id basePatchSetId = PatchSet.Id.fromRef(base);
     if (basePatchSetId != null) {
@@ -119,7 +119,7 @@
     return ret;
   }
 
-  private ChangeNotes notesFor(RevisionResource rsrc, Change.Id id) throws OrmException {
+  private ChangeNotes notesFor(RevisionResource rsrc, Change.Id id) {
     if (rsrc.getChange().getId().equals(id)) {
       return rsrc.getNotes();
     }
@@ -139,11 +139,10 @@
    * @return the commit onto which the patch set should be rebased.
    * @throws RestApiException if rebase is not possible.
    * @throws IOException if accessing the repository fails.
-   * @throws OrmException if accessing the database fails.
    */
   public ObjectId findBaseRevision(
       PatchSet patchSet, Branch.NameKey destBranch, Repository git, RevWalk rw)
-      throws RestApiException, IOException, OrmException {
+      throws RestApiException, IOException {
     String baseRev = null;
     RevCommit commit = rw.parseCommit(ObjectId.fromString(patchSet.getRevision().get()));
 
diff --git a/java/com/google/gerrit/server/change/ReviewerAdder.java b/java/com/google/gerrit/server/change/ReviewerAdder.java
index 21fc03c..a6ad559 100644
--- a/java/com/google/gerrit/server/change/ReviewerAdder.java
+++ b/java/com/google/gerrit/server/change/ReviewerAdder.java
@@ -68,7 +68,6 @@
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.Context;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -188,14 +187,13 @@
    * @return handle describing the addition operation. If the {@code op} field is present, this
    *     operation may be added to a {@code BatchUpdate}. Otherwise, the {@code error} field
    *     contains information about an error that occurred
-   * @throws OrmException
    * @throws IOException
    * @throws PermissionBackendException
    * @throws ConfigInvalidException
    */
   public ReviewerAddition prepare(
       ChangeNotes notes, CurrentUser user, AddReviewerInput input, boolean allowGroup)
-      throws OrmException, IOException, PermissionBackendException, ConfigInvalidException {
+      throws IOException, PermissionBackendException, ConfigInvalidException {
     requireNonNull(input.reviewer);
     boolean confirmed = input.confirmed();
     boolean allowByEmail =
@@ -245,7 +243,7 @@
   @Nullable
   private ReviewerAddition addByAccountId(
       AddReviewerInput input, ChangeNotes notes, CurrentUser user)
-      throws OrmException, PermissionBackendException, IOException, ConfigInvalidException {
+      throws PermissionBackendException, IOException, ConfigInvalidException {
     IdentifiedUser reviewerUser;
     boolean exactMatchFound = false;
     try {
@@ -449,7 +447,7 @@
           : ImmutableSet.of();
     }
 
-    public void gatherResults(ChangeData cd) throws OrmException, PermissionBackendException {
+    public void gatherResults(ChangeData cd) throws PermissionBackendException {
       checkState(op != null, "addition did not result in an update op");
       checkState(op.getResult() != null, "op did not return a result");
 
@@ -510,7 +508,7 @@
       CurrentUser user,
       Iterable<? extends AddReviewerInput> inputs,
       boolean allowGroup)
-      throws OrmException, IOException, PermissionBackendException, ConfigInvalidException {
+      throws IOException, PermissionBackendException, ConfigInvalidException {
     // Process CC ops before reviewer ops, so a user that appears in both lists ends up as a
     // reviewer; the last call to ChangeUpdate#putReviewer wins. This can happen if the caller
     // specifies the same string twice, or less obviously if they specify multiple groups with
@@ -558,7 +556,7 @@
     // We never call updateRepo on the addition ops, which is only ok because it's a no-op.
 
     public void updateChange(ChangeContext ctx, PatchSet patchSet)
-        throws OrmException, RestApiException, IOException {
+        throws RestApiException, IOException {
       for (ReviewerAddition addition : additions()) {
         addition.op.setPatchSet(patchSet);
         addition.op.updateChange(ctx);
diff --git a/java/com/google/gerrit/server/change/ReviewerJson.java b/java/com/google/gerrit/server/change/ReviewerJson.java
index fd5772d..2742bb9 100644
--- a/java/com/google/gerrit/server/change/ReviewerJson.java
+++ b/java/com/google/gerrit/server/change/ReviewerJson.java
@@ -35,7 +35,6 @@
 import com.google.gerrit.server.project.SubmitRuleEvaluator;
 import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.Collection;
@@ -65,7 +64,7 @@
   }
 
   public List<ReviewerInfo> format(Collection<ReviewerResource> rsrcs)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     List<ReviewerInfo> infos = Lists.newArrayListWithCapacity(rsrcs.size());
     AccountLoader loader = accountLoaderFactory.create(true);
     ChangeData cd = null;
@@ -88,13 +87,12 @@
     return infos;
   }
 
-  public List<ReviewerInfo> format(ReviewerResource rsrc)
-      throws OrmException, PermissionBackendException {
+  public List<ReviewerInfo> format(ReviewerResource rsrc) throws PermissionBackendException {
     return format(ImmutableList.of(rsrc));
   }
 
   public ReviewerInfo format(ReviewerInfo out, Account.Id reviewerAccountId, ChangeData cd)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     PatchSet.Id psId = cd.change().currentPatchSetId();
     return format(
         out,
@@ -108,7 +106,7 @@
       Account.Id reviewerAccountId,
       ChangeData cd,
       Iterable<PatchSetApproval> approvals)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     LabelTypes labelTypes = cd.getLabelTypes();
 
     out.approvals = new TreeMap<>(labelTypes.nameComparator());
diff --git a/java/com/google/gerrit/server/change/RevisionJson.java b/java/com/google/gerrit/server/change/RevisionJson.java
index 42175eb..aa733cd 100644
--- a/java/com/google/gerrit/server/change/RevisionJson.java
+++ b/java/com/google/gerrit/server/change/RevisionJson.java
@@ -65,7 +65,6 @@
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
@@ -155,8 +154,7 @@
    * depending on the options provided when constructing this instance.
    */
   public RevisionInfo getRevisionInfo(ChangeData cd, PatchSet in)
-      throws PatchListNotAvailableException, GpgException, OrmException, IOException,
-          PermissionBackendException {
+      throws PatchListNotAvailableException, GpgException, IOException, PermissionBackendException {
     AccountLoader accountLoader = accountLoaderFactory.create(has(DETAILED_ACCOUNTS));
     try (Repository repo = openRepoIfNecessary(cd.project());
         RevWalk rw = newRevWalk(repo)) {
@@ -213,8 +211,7 @@
       Map<PatchSet.Id, PatchSet> map,
       Optional<PatchSet.Id> limitToPsId,
       ChangeInfo changeInfo)
-      throws PatchListNotAvailableException, GpgException, OrmException, IOException,
-          PermissionBackendException {
+      throws PatchListNotAvailableException, GpgException, IOException, PermissionBackendException {
     Map<String, RevisionInfo> res = new LinkedHashMap<>();
     try (Repository repo = openRepoIfNecessary(cd.project());
         RevWalk rw = newRevWalk(repo)) {
@@ -239,7 +236,7 @@
   }
 
   private Map<String, FetchInfo> makeFetchMap(ChangeData cd, PatchSet in)
-      throws PermissionBackendException, OrmException, IOException {
+      throws PermissionBackendException, IOException {
     Map<String, FetchInfo> r = new LinkedHashMap<>();
     for (Extension<DownloadScheme> e : downloadSchemes) {
       String schemeName = e.getExportName();
@@ -275,8 +272,7 @@
       @Nullable RevWalk rw,
       boolean fillCommit,
       @Nullable ChangeInfo changeInfo)
-      throws PatchListNotAvailableException, GpgException, OrmException, IOException,
-          PermissionBackendException {
+      throws PatchListNotAvailableException, GpgException, IOException, PermissionBackendException {
     Change c = cd.change();
     RevisionInfo out = new RevisionInfo();
     out.isCurrent = in.getId().equals(c.currentPatchSetId());
@@ -350,14 +346,13 @@
    *     lazyload}.
    */
   private PermissionBackend.ForChange permissionBackendForChange(
-      PermissionBackend.WithUser withUser, ChangeData cd) throws OrmException {
+      PermissionBackend.WithUser withUser, ChangeData cd) {
     return lazyLoad
         ? withUser.change(cd)
         : withUser.indexedChange(cd, notesFactory.createFromIndexedChange(cd.change()));
   }
 
-  private boolean isWorldReadable(ChangeData cd)
-      throws OrmException, PermissionBackendException, IOException {
+  private boolean isWorldReadable(ChangeData cd) throws PermissionBackendException, IOException {
     try {
       permissionBackendForChange(permissionBackend.user(anonymous), cd)
           .check(ChangePermission.READ);
diff --git a/java/com/google/gerrit/server/change/SetAssigneeOp.java b/java/com/google/gerrit/server/change/SetAssigneeOp.java
index dd24ff6..8d350c3 100644
--- a/java/com/google/gerrit/server/change/SetAssigneeOp.java
+++ b/java/com/google/gerrit/server/change/SetAssigneeOp.java
@@ -32,7 +32,6 @@
 import com.google.gerrit.server.update.Context;
 import com.google.gerrit.server.validators.AssigneeValidationListener;
 import com.google.gerrit.server.validators.ValidationException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
@@ -74,7 +73,7 @@
   }
 
   @Override
-  public boolean updateChange(ChangeContext ctx) throws OrmException, RestApiException {
+  public boolean updateChange(ChangeContext ctx) throws RestApiException {
     change = ctx.getChange();
     if (newAssignee.getAccountId().equals(change.getAssignee())) {
       return false;
@@ -117,7 +116,7 @@
   }
 
   @Override
-  public void postUpdate(Context ctx) throws OrmException {
+  public void postUpdate(Context ctx) {
     try {
       SetAssigneeSender cm =
           setAssigneeSenderFactory.create(
diff --git a/java/com/google/gerrit/server/change/SetHashtagsOp.java b/java/com/google/gerrit/server/change/SetHashtagsOp.java
index 4f73053..abc4eee 100644
--- a/java/com/google/gerrit/server/change/SetHashtagsOp.java
+++ b/java/com/google/gerrit/server/change/SetHashtagsOp.java
@@ -38,7 +38,6 @@
 import com.google.gerrit.server.update.Context;
 import com.google.gerrit.server.validators.HashtagValidationListener;
 import com.google.gerrit.server.validators.ValidationException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
@@ -82,8 +81,7 @@
 
   @Override
   public boolean updateChange(ChangeContext ctx)
-      throws AuthException, BadRequestException, MethodNotAllowedException, OrmException,
-          IOException {
+      throws AuthException, BadRequestException, MethodNotAllowedException, IOException {
     if (input == null || (input.add == null && input.remove == null)) {
       updatedHashtags = ImmutableSortedSet.of();
       return false;
@@ -146,7 +144,7 @@
   }
 
   @Override
-  public void postUpdate(Context ctx) throws OrmException {
+  public void postUpdate(Context ctx) {
     if (updated() && fireEvent) {
       hashtagsEdited.fire(
           change, ctx.getAccount(), updatedHashtags, toAdd, toRemove, ctx.getWhen());
diff --git a/java/com/google/gerrit/server/change/SetPrivateOp.java b/java/com/google/gerrit/server/change/SetPrivateOp.java
index 017dbec..1600fd5 100644
--- a/java/com/google/gerrit/server/change/SetPrivateOp.java
+++ b/java/com/google/gerrit/server/change/SetPrivateOp.java
@@ -30,7 +30,6 @@
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.Context;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
@@ -75,7 +74,7 @@
 
   @Override
   public boolean updateChange(ChangeContext ctx)
-      throws ResourceConflictException, OrmException, BadRequestException {
+      throws ResourceConflictException, BadRequestException {
     change = ctx.getChange();
     if (ctx.getChange().isPrivate() == isPrivate) {
       // No-op
diff --git a/java/com/google/gerrit/server/change/WalkSorter.java b/java/com/google/gerrit/server/change/WalkSorter.java
index 916a62b..5945a0c 100644
--- a/java/com/google/gerrit/server/change/WalkSorter.java
+++ b/java/com/google/gerrit/server/change/WalkSorter.java
@@ -24,11 +24,11 @@
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.Ordering;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.ArrayDeque;
@@ -73,7 +73,7 @@
                 }
                 try {
                   return in.get(0).data().change().getProject();
-                } catch (OrmException e) {
+                } catch (StorageException e) {
                   throw new IllegalStateException(e);
                 }
               });
@@ -98,7 +98,7 @@
     return this;
   }
 
-  public Iterable<PatchSetData> sort(Iterable<ChangeData> in) throws OrmException, IOException {
+  public Iterable<PatchSetData> sort(Iterable<ChangeData> in) throws IOException {
     ListMultimap<Project.NameKey, ChangeData> byProject =
         MultimapBuilder.hashKeys().arrayListValues().build();
     for (ChangeData cd : in) {
@@ -114,7 +114,7 @@
   }
 
   private List<PatchSetData> sortProject(Project.NameKey project, Collection<ChangeData> in)
-      throws OrmException, IOException {
+      throws IOException {
     try (Repository repo = repoManager.openRepository(project);
         RevWalk rw = new RevWalk(repo)) {
       rw.setRetainBody(retainBody);
@@ -217,7 +217,7 @@
   }
 
   private ListMultimap<RevCommit, PatchSetData> byCommit(RevWalk rw, Collection<ChangeData> in)
-      throws OrmException, IOException {
+      throws IOException {
     ListMultimap<RevCommit, PatchSetData> byCommit =
         MultimapBuilder.hashKeys(in.size()).arrayListValues(1).build();
     for (ChangeData cd : in) {
diff --git a/java/com/google/gerrit/server/change/WorkInProgressOp.java b/java/com/google/gerrit/server/change/WorkInProgressOp.java
index e5fdd8f..f3f1a29 100644
--- a/java/com/google/gerrit/server/change/WorkInProgressOp.java
+++ b/java/com/google/gerrit/server/change/WorkInProgressOp.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.update.BatchUpdateOp;
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.Context;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
@@ -85,7 +84,7 @@
   }
 
   @Override
-  public boolean updateChange(ChangeContext ctx) throws OrmException {
+  public boolean updateChange(ChangeContext ctx) {
     change = ctx.getChange();
     notes = ctx.getNotes();
     ps = psUtil.get(ctx.getNotes(), change.currentPatchSetId());
diff --git a/java/com/google/gerrit/server/config/ScheduleConfig.java b/java/com/google/gerrit/server/config/ScheduleConfig.java
index c5f53b3..963107a2 100644
--- a/java/com/google/gerrit/server/config/ScheduleConfig.java
+++ b/java/com/google/gerrit/server/config/ScheduleConfig.java
@@ -91,7 +91,7 @@
  *       executions are {@code Wed 10:30}, {@code Fri 10:30}. etc.
  *   <li>
  *       <pre>
- * foo.startTime = 6:00
+ * foo.startTime = 06:00
  * foo.interval = 1 day
  * </pre>
  *       Assuming that the server is started on {@code Mon 7:00} then this yields the first run on
@@ -174,7 +174,18 @@
       return true;
     }
 
-    if (interval <= 0 || initialDelay < 0) {
+    if (interval != INVALID_CONFIG && interval <= 0) {
+      logger.atSevere().log("Invalid interval value \"%d\" for \"%s\": must be > 0", interval, key);
+      interval = INVALID_CONFIG;
+    }
+
+    if (initialDelay != INVALID_CONFIG && initialDelay < 0) {
+      logger.atSevere().log(
+          "Invalid initial delay value \"%d\" for \"%s\": must be >= 0", initialDelay, key);
+      initialDelay = INVALID_CONFIG;
+    }
+
+    if (interval == INVALID_CONFIG || initialDelay == INVALID_CONFIG) {
       logger.atSevere().log("Invalid schedule configuration for \"%s\" is ignored. ", key);
       return true;
     }
@@ -216,6 +227,9 @@
       return ConfigUtil.getTimeUnit(
           rc, section, subsection, keyInterval, MISSING_CONFIG, TimeUnit.MILLISECONDS);
     } catch (IllegalArgumentException e) {
+      // We only need to log the exception message; it already includes the
+      // section.subsection.key and bad value.
+      logger.atSevere().log("%s", e.getMessage());
       return INVALID_CONFIG;
     }
   }
@@ -258,6 +272,7 @@
       }
       return delay;
     } catch (DateTimeParseException e) {
+      logger.atSevere().log("Invalid start time: %s", e.getMessage());
       return INVALID_CONFIG;
     }
   }
diff --git a/java/com/google/gerrit/server/edit/ChangeEditModifier.java b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
index 9698867..2767109 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditModifier.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
@@ -43,7 +43,6 @@
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.util.CommitMessageUtil;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -114,7 +113,7 @@
    * @throws PermissionBackendException
    */
   public void createEdit(Repository repository, ChangeNotes notes)
-      throws AuthException, IOException, InvalidChangeOperationException, OrmException,
+      throws AuthException, IOException, InvalidChangeOperationException,
           PermissionBackendException, ResourceConflictException {
     assertCanEdit(notes);
 
@@ -142,8 +141,8 @@
    * @throws PermissionBackendException
    */
   public void rebaseEdit(Repository repository, ChangeNotes notes)
-      throws AuthException, InvalidChangeOperationException, IOException, OrmException,
-          MergeConflictException, PermissionBackendException, ResourceConflictException {
+      throws AuthException, InvalidChangeOperationException, IOException, MergeConflictException,
+          PermissionBackendException, ResourceConflictException {
     assertCanEdit(notes);
 
     Optional<ChangeEdit> optionalChangeEdit = lookupChangeEdit(notes);
@@ -207,7 +206,7 @@
    * @throws BadRequestException if the commit message is malformed
    */
   public void modifyMessage(Repository repository, ChangeNotes notes, String newCommitMessage)
-      throws AuthException, IOException, UnchangedCommitMessageException, OrmException,
+      throws AuthException, IOException, UnchangedCommitMessageException,
           PermissionBackendException, BadRequestException, ResourceConflictException {
     assertCanEdit(notes);
     newCommitMessage = CommitMessageUtil.checkAndSanitizeCommitMessage(newCommitMessage);
@@ -250,7 +249,7 @@
    */
   public void modifyFile(
       Repository repository, ChangeNotes notes, String filePath, RawInput newContent)
-      throws AuthException, InvalidChangeOperationException, IOException, OrmException,
+      throws AuthException, InvalidChangeOperationException, IOException,
           PermissionBackendException, ResourceConflictException {
     modifyTree(repository, notes, new ChangeFileContentModification(filePath, newContent));
   }
@@ -268,7 +267,7 @@
    * @throws ResourceConflictException if the project state does not permit the operation
    */
   public void deleteFile(Repository repository, ChangeNotes notes, String file)
-      throws AuthException, InvalidChangeOperationException, IOException, OrmException,
+      throws AuthException, InvalidChangeOperationException, IOException,
           PermissionBackendException, ResourceConflictException {
     modifyTree(repository, notes, new DeleteFileModification(file));
   }
@@ -289,7 +288,7 @@
    */
   public void renameFile(
       Repository repository, ChangeNotes notes, String currentFilePath, String newFilePath)
-      throws AuthException, InvalidChangeOperationException, IOException, OrmException,
+      throws AuthException, InvalidChangeOperationException, IOException,
           PermissionBackendException, ResourceConflictException {
     modifyTree(repository, notes, new RenameFileModification(currentFilePath, newFilePath));
   }
@@ -307,14 +306,14 @@
    * @throws PermissionBackendException
    */
   public void restoreFile(Repository repository, ChangeNotes notes, String file)
-      throws AuthException, InvalidChangeOperationException, IOException, OrmException,
+      throws AuthException, InvalidChangeOperationException, IOException,
           PermissionBackendException, ResourceConflictException {
     modifyTree(repository, notes, new RestoreFileModification(file));
   }
 
   private void modifyTree(
       Repository repository, ChangeNotes notes, TreeModification treeModification)
-      throws AuthException, IOException, OrmException, InvalidChangeOperationException,
+      throws AuthException, IOException, InvalidChangeOperationException,
           PermissionBackendException, ResourceConflictException {
     assertCanEdit(notes);
 
@@ -360,7 +359,7 @@
       PatchSet patchSet,
       List<TreeModification> treeModifications)
       throws AuthException, IOException, InvalidChangeOperationException, MergeConflictException,
-          OrmException, PermissionBackendException, ResourceConflictException {
+          PermissionBackendException, ResourceConflictException {
     assertCanEdit(notes);
 
     Optional<ChangeEdit> optionalChangeEdit = lookupChangeEdit(notes);
@@ -391,8 +390,7 @@
   }
 
   private void assertCanEdit(ChangeNotes notes)
-      throws AuthException, PermissionBackendException, IOException, ResourceConflictException,
-          OrmException {
+      throws AuthException, PermissionBackendException, IOException, ResourceConflictException {
     if (!currentUser.get().isIdentifiedUser()) {
       throw new AuthException("Authentication required");
     }
@@ -442,13 +440,12 @@
     return changeEditUtil.byChange(notes);
   }
 
-  private PatchSet getBasePatchSet(Optional<ChangeEdit> optionalChangeEdit, ChangeNotes notes)
-      throws OrmException {
+  private PatchSet getBasePatchSet(Optional<ChangeEdit> optionalChangeEdit, ChangeNotes notes) {
     Optional<PatchSet> editBasePatchSet = optionalChangeEdit.map(ChangeEdit::getBasePatchSet);
     return editBasePatchSet.isPresent() ? editBasePatchSet.get() : lookupCurrentPatchSet(notes);
   }
 
-  private PatchSet lookupCurrentPatchSet(ChangeNotes notes) throws OrmException {
+  private PatchSet lookupCurrentPatchSet(ChangeNotes notes) {
     return patchSetUtil.current(notes);
   }
 
diff --git a/java/com/google/gerrit/server/edit/ChangeEditUtil.java b/java/com/google/gerrit/server/edit/ChangeEditUtil.java
index 898f427..cb8147c 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditUtil.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditUtil.java
@@ -16,6 +16,7 @@
 
 import static com.google.common.base.Preconditions.checkArgument;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.ChangeKind;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -38,7 +39,6 @@
 import com.google.gerrit.server.update.RepoContext;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -146,7 +146,6 @@
    * @param notify Notify handling that defines to whom email notifications should be sent after the
    *     change edit is published.
    * @throws IOException
-   * @throws OrmException
    * @throws UpdateException
    * @throws RestApiException
    */
@@ -156,7 +155,7 @@
       CurrentUser user,
       ChangeEdit edit,
       NotifyResolver.Result notify)
-      throws IOException, OrmException, RestApiException, UpdateException {
+      throws IOException, RestApiException, UpdateException {
     Change change = edit.getChange();
     try (Repository repo = gitManager.openRepository(change.getProject());
         ObjectInserter oi = repo.newObjectInserter();
@@ -210,9 +209,8 @@
    *
    * @param edit change edit to delete
    * @throws IOException
-   * @throws OrmException
    */
-  public void delete(ChangeEdit edit) throws IOException, OrmException {
+  public void delete(ChangeEdit edit) throws IOException {
     Change change = edit.getChange();
     try (Repository repo = gitManager.openRepository(change.getProject())) {
       deleteRef(repo, edit);
@@ -226,7 +224,7 @@
       checkArgument(pos > 0, "invalid edit ref: %s", ref.getName());
       String psId = ref.getName().substring(pos + 1);
       return psUtil.get(notes, new PatchSet.Id(notes.getChange().getId(), Integer.parseInt(psId)));
-    } catch (OrmException | NumberFormatException e) {
+    } catch (StorageException | NumberFormatException e) {
       throw new IOException(e);
     }
   }
diff --git a/java/com/google/gerrit/server/events/EventBroker.java b/java/com/google/gerrit/server/events/EventBroker.java
index 03b5d54..d5f548f 100644
--- a/java/com/google/gerrit/server/events/EventBroker.java
+++ b/java/com/google/gerrit/server/events/EventBroker.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -77,8 +76,7 @@
   }
 
   @Override
-  public void postEvent(Change change, ChangeEvent event)
-      throws OrmException, PermissionBackendException {
+  public void postEvent(Change change, ChangeEvent event) throws PermissionBackendException {
     fireEvent(change, event);
   }
 
@@ -94,7 +92,7 @@
   }
 
   @Override
-  public void postEvent(Event event) throws OrmException, PermissionBackendException {
+  public void postEvent(Event event) throws PermissionBackendException {
     fireEvent(event);
   }
 
@@ -102,8 +100,7 @@
     unrestrictedListeners.runEach(l -> l.onEvent(event));
   }
 
-  protected void fireEvent(Change change, ChangeEvent event)
-      throws OrmException, PermissionBackendException {
+  protected void fireEvent(Change change, ChangeEvent event) throws PermissionBackendException {
     for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
       CurrentUser user = c.call(UserScopedEventListener::getUser);
       if (isVisibleTo(change, user)) {
@@ -134,7 +131,7 @@
     fireEventForUnrestrictedListeners(event);
   }
 
-  protected void fireEvent(Event event) throws OrmException, PermissionBackendException {
+  protected void fireEvent(Event event) throws PermissionBackendException {
     for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
       CurrentUser user = c.call(UserScopedEventListener::getUser);
       if (isVisibleTo(event, user)) {
@@ -158,8 +155,7 @@
     }
   }
 
-  protected boolean isVisibleTo(Change change, CurrentUser user)
-      throws OrmException, PermissionBackendException {
+  protected boolean isVisibleTo(Change change, CurrentUser user) throws PermissionBackendException {
     if (change == null) {
       return false;
     }
@@ -193,8 +189,7 @@
     }
   }
 
-  protected boolean isVisibleTo(Event event, CurrentUser user)
-      throws OrmException, PermissionBackendException {
+  protected boolean isVisibleTo(Event event, CurrentUser user) throws PermissionBackendException {
     if (event instanceof RefEvent) {
       RefEvent refEvent = (RefEvent) event;
       String ref = refEvent.getRefName();
diff --git a/java/com/google/gerrit/server/events/EventDispatcher.java b/java/com/google/gerrit/server/events/EventDispatcher.java
index cbf547e..e6735f2 100644
--- a/java/com/google/gerrit/server/events/EventDispatcher.java
+++ b/java/com/google/gerrit/server/events/EventDispatcher.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 
 /** Interface for posting (dispatching) Events */
 public interface EventDispatcher {
@@ -27,10 +26,9 @@
    *
    * @param change The change that the event is related to
    * @param event The event to post
-   * @throws OrmException on failure to post the event due to DB error
    * @throws PermissionBackendException on failure of permission checks
    */
-  void postEvent(Change change, ChangeEvent event) throws OrmException, PermissionBackendException;
+  void postEvent(Change change, ChangeEvent event) throws PermissionBackendException;
 
   /**
    * Post a stream event that is related to a branch
@@ -56,8 +54,7 @@
    * specific postEvent methods for those use cases.
    *
    * @param event The event to post.
-   * @throws OrmException on failure to post the event due to DB error
    * @throws PermissionBackendException on failure of permission checks
    */
-  void postEvent(Event event) throws OrmException, PermissionBackendException;
+  void postEvent(Event event) throws PermissionBackendException;
 }
diff --git a/java/com/google/gerrit/server/events/EventFactory.java b/java/com/google/gerrit/server/events/EventFactory.java
index 7904b28..efd9bb9 100644
--- a/java/com/google/gerrit/server/events/EventFactory.java
+++ b/java/com/google/gerrit/server/events/EventFactory.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.common.data.LabelTypes;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.common.data.SubmitRequirement;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.index.IndexConfig;
 import com.google.gerrit.reviewdb.client.Account;
@@ -63,7 +64,6 @@
 import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -155,7 +155,7 @@
    * @param notes
    * @return object suitable for serialization to JSON
    */
-  public ChangeAttribute asChangeAttribute(Change change, ChangeNotes notes) throws OrmException {
+  public ChangeAttribute asChangeAttribute(Change change, ChangeNotes notes) {
     ChangeAttribute a = asChangeAttribute(change);
     Set<String> hashtags = notes.load().getHashtags();
     if (!hashtags.isEmpty()) {
@@ -200,7 +200,7 @@
    * @param a
    * @param notes
    */
-  public void addAllReviewers(ChangeAttribute a, ChangeNotes notes) throws OrmException {
+  public void addAllReviewers(ChangeAttribute a, ChangeNotes notes) {
     Collection<Account.Id> reviewers = approvalsUtil.getReviewers(notes).all();
     if (!reviewers.isEmpty()) {
       a.allReviewers = Lists.newArrayListWithCapacity(reviewers.size());
@@ -271,7 +271,7 @@
     try {
       addDependsOn(rw, ca, change, currentPs);
       addNeededBy(rw, ca, change, currentPs);
-    } catch (OrmException | IOException e) {
+    } catch (StorageException | IOException e) {
       // Squash DB exceptions and leave dependency lists partially filled.
     }
     // Remove empty lists so a confusing label won't be displayed in the output.
@@ -284,7 +284,7 @@
   }
 
   private void addDependsOn(RevWalk rw, ChangeAttribute ca, Change change, PatchSet currentPs)
-      throws OrmException, IOException {
+      throws IOException {
     RevCommit commit = rw.parseCommit(ObjectId.fromString(currentPs.getRevision().get()));
     final List<String> parentNames = new ArrayList<>(commit.getParentCount());
     for (RevCommit p : commit.getParents()) {
@@ -317,7 +317,7 @@
   }
 
   private void addNeededBy(RevWalk rw, ChangeAttribute ca, Change change, PatchSet currentPs)
-      throws OrmException, IOException {
+      throws IOException {
     if (currentPs.getGroups().isEmpty()) {
       return;
     }
@@ -494,7 +494,7 @@
         }
       }
       p.kind = changeKindCache.getChangeKind(change, patchSet);
-    } catch (IOException | OrmException e) {
+    } catch (IOException | StorageException e) {
       logger.atSevere().withCause(e).log("Cannot load patch set data for %s", patchSet.getId());
     } catch (PatchListObjectTooLargeException e) {
       logger.atWarning().log("Cannot get size information for %s: %s", pId, e.getMessage());
@@ -506,7 +506,7 @@
 
   // TODO: The same method exists in PatchSetInfoFactory, find a common place
   // for it
-  private UserIdentity toUserIdentity(PersonIdent who) throws IOException, OrmException {
+  private UserIdentity toUserIdentity(PersonIdent who) throws IOException {
     UserIdentity u = new UserIdentity();
     u.setName(who.getName());
     u.setEmail(who.getEmailAddress());
diff --git a/java/com/google/gerrit/server/events/StreamEventsApiListener.java b/java/com/google/gerrit/server/events/StreamEventsApiListener.java
index 750b579..3add4ca 100644
--- a/java/com/google/gerrit/server/events/StreamEventsApiListener.java
+++ b/java/com/google/gerrit/server/events/StreamEventsApiListener.java
@@ -20,6 +20,7 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ApprovalInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -55,7 +56,6 @@
 import com.google.gerrit.server.plugincontext.PluginItemContext;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -135,15 +135,15 @@
     this.changeNotesFactory = changeNotesFactory;
   }
 
-  private ChangeNotes getNotes(ChangeInfo info) throws OrmException {
+  private ChangeNotes getNotes(ChangeInfo info) {
     try {
       return changeNotesFactory.createChecked(new Change.Id(info._number));
     } catch (NoSuchChangeException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
   }
 
-  private PatchSet getPatchSet(ChangeNotes notes, RevisionInfo info) throws OrmException {
+  private PatchSet getPatchSet(ChangeNotes notes, RevisionInfo info) {
     return psUtil.get(notes, PatchSet.Id.fromRef(info.ref));
   }
 
@@ -152,7 +152,7 @@
         () -> {
           try {
             return eventFactory.asChangeAttribute(change, notes);
-          } catch (OrmException e) {
+          } catch (StorageException e) {
             throw new RuntimeException(e);
           }
         });
@@ -248,7 +248,7 @@
       event.oldAssignee = accountAttributeSupplier(ev.getOldAssignee());
 
       dispatcher.run(d -> d.postEvent(change, event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to dispatch event");
     }
   }
@@ -265,7 +265,7 @@
       event.oldTopic = ev.getOldTopic();
 
       dispatcher.run(d -> d.postEvent(change, event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to dispatch event");
     }
   }
@@ -283,7 +283,7 @@
       event.uploader = accountAttributeSupplier(ev.getWho());
 
       dispatcher.run(d -> d.postEvent(change, event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to dispatch event");
     }
   }
@@ -303,7 +303,7 @@
           approvalsAttributeSupplier(change, ev.getNewApprovals(), ev.getOldApprovals());
 
       dispatcher.run(d -> d.postEvent(change, event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to dispatch event");
     }
   }
@@ -321,7 +321,7 @@
         event.reviewer = accountAttributeSupplier(reviewer);
         dispatcher.run(d -> d.postEvent(event));
       }
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to dispatch event");
     }
   }
@@ -349,7 +349,7 @@
       event.removed = hashtagArray(ev.getRemovedHashtags());
 
       dispatcher.run(d -> d.postEvent(change, event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to dispatch event");
     }
   }
@@ -386,7 +386,7 @@
       event.approvals = approvalsAttributeSupplier(change, ev.getApprovals(), ev.getOldApprovals());
 
       dispatcher.run(d -> d.postEvent(change, event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to dispatch event");
     }
   }
@@ -404,7 +404,7 @@
       event.reason = ev.getReason();
 
       dispatcher.run(d -> d.postEvent(change, event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to dispatch event");
     }
   }
@@ -422,7 +422,7 @@
       event.newRev = ev.getNewRevisionId();
 
       dispatcher.run(d -> d.postEvent(change, event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to dispatch event");
     }
   }
@@ -440,7 +440,7 @@
       event.reason = ev.getReason();
 
       dispatcher.run(d -> d.postEvent(change, event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to dispatch event");
     }
   }
@@ -458,7 +458,7 @@
       event.patchSet = patchSetAttributeSupplier(change, patchSet);
 
       dispatcher.run(d -> d.postEvent(change, event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to dispatch event");
     }
   }
@@ -476,7 +476,7 @@
       event.patchSet = patchSetAttributeSupplier(change, patchSet);
 
       dispatcher.run(d -> d.postEvent(change, event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to dispatch event");
     }
   }
@@ -496,7 +496,7 @@
       event.approvals = approvalsAttributeSupplier(change, ev.getApprovals(), ev.getOldApprovals());
 
       dispatcher.run(d -> d.postEvent(change, event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to dispatch event");
     }
   }
@@ -512,7 +512,7 @@
       event.deleter = accountAttributeSupplier(ev.getWho());
 
       dispatcher.run(d -> d.postEvent(change, event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to dispatch event");
     }
   }
diff --git a/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java b/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
index 513a5de..fdce1da 100644
--- a/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/AssigneeChanged.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.extensions.events;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -22,7 +23,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.sql.Timestamp;
@@ -53,7 +53,7 @@
               util.accountInfo(oldAssignee),
               when);
       listeners.runEach(l -> l.onAssigneeChanged(event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Couldn't fire event");
     }
   }
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java b/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
index 3d6700e..a8c08b9 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeAbandoned.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.extensions.events;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -28,7 +29,6 @@
 import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -72,7 +72,7 @@
     } catch (PatchListNotAvailableException
         | GpgException
         | IOException
-        | OrmException
+        | StorageException
         | PermissionBackendException e) {
       logger.atSevere().withCause(e).log("Couldn't fire event");
     }
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java b/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java
index d9eb9f9..9e3e979 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeDeleted.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.extensions.events;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -22,7 +23,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.sql.Timestamp;
@@ -47,7 +47,7 @@
     try {
       Event event = new Event(util.changeInfo(change), util.accountInfo(deleter), when);
       listeners.runEach(l -> l.onChangeDeleted(event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Couldn't fire event");
     }
   }
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeMerged.java b/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
index 7b814ae..756f383 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeMerged.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.extensions.events;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -28,7 +29,6 @@
 import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -66,7 +66,7 @@
     } catch (PatchListNotAvailableException
         | GpgException
         | IOException
-        | OrmException
+        | StorageException
         | PermissionBackendException e) {
       logger.atSevere().withCause(e).log("Couldn't fire event");
     }
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeRestored.java b/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
index 81b04cd..e8bed56 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeRestored.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.extensions.events;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -28,7 +29,6 @@
 import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -66,7 +66,7 @@
     } catch (PatchListNotAvailableException
         | GpgException
         | IOException
-        | OrmException
+        | StorageException
         | PermissionBackendException e) {
       logger.atSevere().withCause(e).log("Couldn't fire event");
     }
diff --git a/java/com/google/gerrit/server/extensions/events/ChangeReverted.java b/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
index ac7aac0..ccb17d5 100644
--- a/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
+++ b/java/com/google/gerrit/server/extensions/events/ChangeReverted.java
@@ -15,12 +15,12 @@
 package com.google.gerrit.server.extensions.events;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.events.ChangeRevertedListener;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.sql.Timestamp;
@@ -45,7 +45,7 @@
     try {
       Event event = new Event(util.changeInfo(change), util.changeInfo(revertChange), when);
       listeners.runEach(l -> l.onChangeReverted(event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Couldn't fire event");
     }
   }
diff --git a/java/com/google/gerrit/server/extensions/events/CommentAdded.java b/java/com/google/gerrit/server/extensions/events/CommentAdded.java
index e224540..ea9ae31 100644
--- a/java/com/google/gerrit/server/extensions/events/CommentAdded.java
+++ b/java/com/google/gerrit/server/extensions/events/CommentAdded.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.extensions.events;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ApprovalInfo;
@@ -29,7 +30,6 @@
 import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -76,7 +76,7 @@
     } catch (PatchListNotAvailableException
         | GpgException
         | IOException
-        | OrmException
+        | StorageException
         | PermissionBackendException e) {
       logger.atSevere().withCause(e).log("Couldn't fire event");
     }
diff --git a/java/com/google/gerrit/server/extensions/events/EventUtil.java b/java/com/google/gerrit/server/extensions/events/EventUtil.java
index 30450dd..d00eb31 100644
--- a/java/com/google/gerrit/server/extensions/events/EventUtil.java
+++ b/java/com/google/gerrit/server/extensions/events/EventUtil.java
@@ -32,7 +32,6 @@
 import com.google.gerrit.server.patch.PatchListNotAvailableException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -74,19 +73,17 @@
     this.revisionJsonFactory = revisionJsonFactory;
   }
 
-  public ChangeInfo changeInfo(Change change) throws OrmException {
+  public ChangeInfo changeInfo(Change change) {
     return changeJsonFactory.create(CHANGE_OPTIONS).format(change);
   }
 
   public RevisionInfo revisionInfo(Project project, PatchSet ps)
-      throws OrmException, PatchListNotAvailableException, GpgException, IOException,
-          PermissionBackendException {
+      throws PatchListNotAvailableException, GpgException, IOException, PermissionBackendException {
     return revisionInfo(project.getNameKey(), ps);
   }
 
   public RevisionInfo revisionInfo(Project.NameKey project, PatchSet ps)
-      throws OrmException, PatchListNotAvailableException, GpgException, IOException,
-          PermissionBackendException {
+      throws PatchListNotAvailableException, GpgException, IOException, PermissionBackendException {
     ChangeData cd = changeDataFactory.create(project, ps.getId().getParentKey());
     return revisionJsonFactory.create(CHANGE_OPTIONS).getRevisionInfo(cd, ps);
   }
diff --git a/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java b/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
index ca0edab..65f5b8b 100644
--- a/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
+++ b/java/com/google/gerrit/server/extensions/events/HashtagsEdited.java
@@ -16,6 +16,7 @@
 
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -23,7 +24,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.sql.Timestamp;
@@ -58,7 +58,7 @@
           new Event(
               util.changeInfo(change), util.accountInfo(editor), hashtags, added, removed, when);
       listeners.runEach(l -> l.onHashtagsEdited(event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Couldn't fire event");
     }
   }
diff --git a/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java b/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
index 358667f..49a6091 100644
--- a/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/PrivateStateChanged.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.extensions.events;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -27,7 +28,6 @@
 import com.google.gerrit.server.patch.PatchListNotAvailableException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -58,7 +58,7 @@
               util.accountInfo(account),
               when);
       listeners.runEach(l -> l.onPrivateStateChanged(event));
-    } catch (OrmException
+    } catch (StorageException
         | PatchListNotAvailableException
         | GpgException
         | IOException
diff --git a/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java b/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
index 8e5259c..f9f67f6 100644
--- a/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
+++ b/java/com/google/gerrit/server/extensions/events/ReviewerAdded.java
@@ -16,6 +16,7 @@
 
 import com.google.common.collect.Lists;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -29,7 +30,6 @@
 import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -73,7 +73,7 @@
     } catch (PatchListNotAvailableException
         | GpgException
         | IOException
-        | OrmException
+        | StorageException
         | PermissionBackendException e) {
       logger.atSevere().withCause(e).log("Couldn't fire event");
     }
diff --git a/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java b/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
index 89c8f18..b92f3e6 100644
--- a/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
+++ b/java/com/google/gerrit/server/extensions/events/ReviewerDeleted.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.extensions.events;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ApprovalInfo;
@@ -29,7 +30,6 @@
 import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -80,7 +80,7 @@
     } catch (PatchListNotAvailableException
         | GpgException
         | IOException
-        | OrmException
+        | StorageException
         | PermissionBackendException e) {
       logger.atSevere().withCause(e).log("Couldn't fire event");
     }
diff --git a/java/com/google/gerrit/server/extensions/events/RevisionCreated.java b/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
index 3fd69a2..6fddcfe 100644
--- a/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
+++ b/java/com/google/gerrit/server/extensions/events/RevisionCreated.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.extensions.events;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -29,7 +30,6 @@
 import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -87,7 +87,7 @@
     } catch (PatchListNotAvailableException
         | GpgException
         | IOException
-        | OrmException
+        | StorageException
         | PermissionBackendException e) {
       logger.atSevere().withCause(e).log("Couldn't fire event");
     }
diff --git a/java/com/google/gerrit/server/extensions/events/TopicEdited.java b/java/com/google/gerrit/server/extensions/events/TopicEdited.java
index 8568c0f..9e1ae44 100644
--- a/java/com/google/gerrit/server/extensions/events/TopicEdited.java
+++ b/java/com/google/gerrit/server/extensions/events/TopicEdited.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.extensions.events;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -22,7 +23,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.sql.Timestamp;
@@ -48,7 +48,7 @@
       Event event =
           new Event(util.changeInfo(change), util.accountInfo(account), oldTopicName, when);
       listeners.runEach(l -> l.onTopicEdited(event));
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Couldn't fire event");
     }
   }
diff --git a/java/com/google/gerrit/server/extensions/events/VoteDeleted.java b/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
index b750851..bd6873a 100644
--- a/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
+++ b/java/com/google/gerrit/server/extensions/events/VoteDeleted.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.extensions.events;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ApprovalInfo;
@@ -29,7 +30,6 @@
 import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -80,7 +80,7 @@
     } catch (PatchListNotAvailableException
         | GpgException
         | IOException
-        | OrmException
+        | StorageException
         | PermissionBackendException e) {
       logger.atSevere().withCause(e).log("Couldn't fire event");
     }
diff --git a/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java b/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
index 6273ad6..785d6fe 100644
--- a/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
+++ b/java/com/google/gerrit/server/extensions/events/WorkInProgressStateChanged.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.extensions.events;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -27,7 +28,6 @@
 import com.google.gerrit.server.patch.PatchListNotAvailableException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -59,7 +59,7 @@
               util.accountInfo(account),
               when);
       listeners.runEach(l -> l.onWorkInProgressStateChanged(event));
-    } catch (OrmException
+    } catch (StorageException
         | PatchListNotAvailableException
         | GpgException
         | IOException
diff --git a/java/com/google/gerrit/server/git/BanCommit.java b/java/com/google/gerrit/server/git/BanCommit.java
index 3cb771e..d8aeece 100644
--- a/java/com/google/gerrit/server/git/BanCommit.java
+++ b/java/com/google/gerrit/server/git/BanCommit.java
@@ -75,7 +75,7 @@
   private final GitRepositoryManager repoManager;
   private final TimeZone tz;
   private final PermissionBackend permissionBackend;
-  private NotesBranchUtil.Factory notesBranchUtilFactory;
+  private final NotesBranchUtil.Factory notesBranchUtilFactory;
 
   @Inject
   BanCommit(
diff --git a/java/com/google/gerrit/server/git/GroupCollector.java b/java/com/google/gerrit/server/git/GroupCollector.java
index 3f037d2..07c995f 100644
--- a/java/com/google/gerrit/server/git/GroupCollector.java
+++ b/java/com/google/gerrit/server/git/GroupCollector.java
@@ -33,7 +33,6 @@
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
 import java.util.ArrayDeque;
 import java.util.Collection;
 import java.util.Deque;
@@ -89,7 +88,7 @@
   }
 
   private interface Lookup {
-    List<String> lookup(PatchSet.Id psId) throws OrmException;
+    List<String> lookup(PatchSet.Id psId);
   }
 
   private final ListMultimap<ObjectId, PatchSet.Id> patchSetsBySha;
@@ -198,7 +197,7 @@
     }
   }
 
-  public SortedSetMultimap<ObjectId, String> getGroups() throws OrmException {
+  public SortedSetMultimap<ObjectId, String> getGroups() {
     done = true;
     SortedSetMultimap<ObjectId, String> result =
         MultimapBuilder.hashKeys(groups.keySet().size()).treeSetValues().build();
@@ -224,8 +223,7 @@
     return id != null && patchSetsBySha.containsKey(id);
   }
 
-  private Set<String> resolveGroups(ObjectId forCommit, Collection<String> candidates)
-      throws OrmException {
+  private Set<String> resolveGroups(ObjectId forCommit, Collection<String> candidates) {
     Set<String> actual = Sets.newTreeSet();
     Set<String> done = Sets.newHashSetWithExpectedSize(candidates.size());
     Set<String> seen = Sets.newHashSetWithExpectedSize(candidates.size());
@@ -260,7 +258,7 @@
     }
   }
 
-  private Iterable<String> resolveGroup(ObjectId forCommit, String group) throws OrmException {
+  private Iterable<String> resolveGroup(ObjectId forCommit, String group) {
     ObjectId id = parseGroup(forCommit, group);
     if (id != null) {
       PatchSet.Id psId = Iterables.getFirst(patchSetsBySha.get(id), null);
diff --git a/java/com/google/gerrit/server/git/MergeUtil.java b/java/com/google/gerrit/server/git/MergeUtil.java
index 5d7a27b..5e2ad47 100644
--- a/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/java/com/google/gerrit/server/git/MergeUtil.java
@@ -30,6 +30,7 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.FooterConstants;
 import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.registration.DynamicSet;
 import com.google.gerrit.extensions.registration.Extension;
@@ -56,7 +57,6 @@
 import com.google.gerrit.server.submit.IntegrationException;
 import com.google.gerrit.server.submit.MergeIdenticalTreeException;
 import com.google.gerrit.server.submit.MergeSorter;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
@@ -237,7 +237,7 @@
     List<CodeReviewCommit> result = new ArrayList<>();
     try {
       result.addAll(mergeSorter.sort(toSort));
-    } catch (IOException | OrmException e) {
+    } catch (IOException | StorageException e) {
       throw new IntegrationException("Branch head sorting failed", e);
     }
     result.sort(CodeReviewCommit.ORDER);
@@ -316,6 +316,7 @@
     return commit;
   }
 
+  @SuppressWarnings("resource") // TemporaryBuffer requires calling close before reading.
   public static ObjectId mergeWithConflicts(
       RevWalk rw,
       ObjectInserter ins,
@@ -349,13 +350,20 @@
     Map<String, ObjectId> resolved = new HashMap<>();
     for (Map.Entry<String, MergeResult<? extends Sequence>> entry : mergeResults.entrySet()) {
       MergeResult<? extends Sequence> p = entry.getValue();
-      try (TemporaryBuffer buf = new TemporaryBuffer.LocalFile(null, 10 * 1024 * 1024)) {
+      TemporaryBuffer buf = null;
+      try {
+        // TODO(dborowitz): Respect inCoreLimit here.
+        buf = new TemporaryBuffer.LocalFile(null, 10 * 1024 * 1024);
         fmt.formatMerge(buf, p, "BASE", oursNameFormatted, theirsNameFormatted, UTF_8);
-        buf.close();
+        buf.close(); // Flush file and close for writes, but leave available for reading.
 
         try (InputStream in = buf.openInputStream()) {
           resolved.put(entry.getKey(), ins.insert(Constants.OBJ_BLOB, buf.length(), in));
         }
+      } finally {
+        if (buf != null) {
+          buf.destroy();
+        }
       }
     }
 
@@ -601,7 +609,7 @@
   private Iterable<PatchSetApproval> safeGetApprovals(ChangeNotes notes, PatchSet.Id psId) {
     try {
       return approvalsUtil.byPatchSet(notes, psId, null, null);
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Can't read approval records for %s", psId);
       return Collections.emptyList();
     }
@@ -714,7 +722,7 @@
       throws IntegrationException {
     try {
       return !mergeSorter.sort(Collections.singleton(toMerge)).contains(toMerge);
-    } catch (IOException | OrmException e) {
+    } catch (IOException | StorageException e) {
       throw new IntegrationException("Branch head sorting failed", e);
     }
   }
diff --git a/java/com/google/gerrit/server/git/MergedByPushOp.java b/java/com/google/gerrit/server/git/MergedByPushOp.java
index 55bb222a..43dc061 100644
--- a/java/com/google/gerrit/server/git/MergedByPushOp.java
+++ b/java/com/google/gerrit/server/git/MergedByPushOp.java
@@ -35,7 +35,6 @@
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.Context;
 import com.google.gerrit.server.util.RequestScopePropagator;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
@@ -105,7 +104,7 @@
   }
 
   @Override
-  public boolean updateChange(ChangeContext ctx) throws OrmException, IOException {
+  public boolean updateChange(ChangeContext ctx) throws IOException {
     change = ctx.getChange();
     correctBranch = refName.equals(change.getDest().get());
     if (!correctBranch) {
@@ -193,7 +192,7 @@
         change, patchSet, ctx.getAccount(), patchSet.getRevision().get(), ctx.getWhen());
   }
 
-  private PatchSetInfo getPatchSetInfo(ChangeContext ctx) throws IOException, OrmException {
+  private PatchSetInfo getPatchSetInfo(ChangeContext ctx) throws IOException {
     RevWalk rw = ctx.getRevWalk();
     RevCommit commit =
         rw.parseCommit(ObjectId.fromString(requireNonNull(patchSet).getRevision().get()));
diff --git a/java/com/google/gerrit/server/git/PureRevertCache.java b/java/com/google/gerrit/server/git/PureRevertCache.java
index 53f004f..1daa1d5 100644
--- a/java/com/google/gerrit/server/git/PureRevertCache.java
+++ b/java/com/google/gerrit/server/git/PureRevertCache.java
@@ -30,7 +30,6 @@
 import com.google.gerrit.server.logging.TraceContext;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Module;
 import com.google.inject.Singleton;
@@ -87,11 +86,9 @@
    *
    * @return {@code true} if {@code claimedRevert} is a pure (clean) revert.
    * @throws IOException if there was a problem with the storage layer
-   * @throws OrmException if there was a problem with the storage layer
    * @throws BadRequestException if there is a problem with the provided {@link ChangeNotes}
    */
-  public boolean isPureRevert(ChangeNotes claimedRevert)
-      throws OrmException, IOException, BadRequestException {
+  public boolean isPureRevert(ChangeNotes claimedRevert) throws IOException, BadRequestException {
     if (claimedRevert.getChange().getRevertOf() == null) {
       throw new BadRequestException("revertOf not set");
     }
diff --git a/java/com/google/gerrit/server/git/receive/BUILD b/java/com/google/gerrit/server/git/receive/BUILD
index f762611..da21fbb 100644
--- a/java/com/google/gerrit/server/git/receive/BUILD
+++ b/java/com/google/gerrit/server/git/receive/BUILD
@@ -5,6 +5,7 @@
     deps = [
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/metrics",
         "//java/com/google/gerrit/reviewdb:server",
@@ -14,7 +15,6 @@
         "//java/com/google/gerrit/util/cli",
         "//lib:args4j",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib/auto:auto-value",
         "//lib/auto:auto-value-annotations",
         "//lib/flogger:api",
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 103edd8..059d028 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -64,6 +64,7 @@
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.HashtagsInput;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.RecipientType;
@@ -158,7 +159,6 @@
 import com.google.gerrit.server.util.RequestScopePropagator;
 import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.gerrit.util.cli.CmdLineParser;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
@@ -897,7 +897,7 @@
         addError(e.getMessage());
         reject(magicBranchCmd, "conflict");
       } catch (RestApiException
-          | OrmException
+          | StorageException
           | UpdateException
           | IOException
           | ConfigInvalidException
@@ -1842,7 +1842,7 @@
     if (magicBranch.deprecatedTopicSeen) {
       messages.add(
           new ValidationMessage(
-              "WARNING: deprecated topic syntax. Use %topic=TOPIC instead", false));
+              "WARNING: deprecated topic syntax. Use -o topic=TOPIC instead", false));
       logger.atInfo().log("deprecated topic push seen for project %s", project.getName());
     }
 
@@ -1938,7 +1938,7 @@
       logger.atSevere().withCause(e).log("Change not found %s", changeId);
       reject(cmd, "change " + changeId + " not found");
       return;
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Cannot lookup existing change %s", changeId);
       reject(cmd, "database error");
       return;
@@ -2237,7 +2237,7 @@
       magicBranch.cmd.setResult(REJECTED_MISSING_OBJECT);
       logger.atSevere().withCause(e).log("Invalid pack upload; one or more objects weren't sent");
       return Collections.emptyList();
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Cannot query database to locate prior changes");
       reject(magicBranch.cmd, "database error");
       return Collections.emptyList();
@@ -2267,14 +2267,14 @@
         update.groups = ImmutableList.copyOf((groups.get(update.commit)));
       }
       logger.atFine().log("Finished updating groups from GroupCollector");
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Error collecting groups for changes");
       reject(magicBranch.cmd, "internal server error");
     }
     return newChanges;
   }
 
-  private boolean foundInExistingRef(Collection<Ref> existingRefs) throws OrmException {
+  private boolean foundInExistingRef(Collection<Ref> existingRefs) {
     for (Ref ref : existingRefs) {
       ChangeNotes notes =
           notesFactory.create(project.getNameKey(), Change.Id.fromRef(ref.getName()));
@@ -2394,11 +2394,11 @@
     }
   }
 
-  private ChangeLookup lookupByChangeKey(RevCommit c, Change.Key key) throws OrmException {
+  private ChangeLookup lookupByChangeKey(RevCommit c, Change.Key key) {
     return new ChangeLookup(c, key, queryProvider.get().byBranchKey(magicBranch.dest, key));
   }
 
-  private ChangeLookup lookupByCommit(RevCommit c) throws OrmException {
+  private ChangeLookup lookupByCommit(RevCommit c) {
     return new ChangeLookup(
         c, null, queryProvider.get().byBranchCommit(magicBranch.dest, c.getName()));
   }
@@ -2525,7 +2525,7 @@
   }
 
   private void submit(Collection<CreateRequest> create, Collection<ReplaceRequest> replace)
-      throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException,
+      throws RestApiException, UpdateException, IOException, ConfigInvalidException,
           PermissionBackendException {
     Map<ObjectId, Change> bySha = Maps.newHashMapWithExpectedSize(create.size() + replace.size());
     for (CreateRequest r : create) {
@@ -2558,7 +2558,7 @@
           req.validateNewPatchSet();
         }
       }
-    } catch (OrmException err) {
+    } catch (StorageException err) {
       logger.atSevere().withCause(err).log(
           "Cannot read database before replacement for project %s", project.getName());
       rejectRemainingRequests(replaceByChange.values(), "internal server error");
@@ -2582,7 +2582,7 @@
     }
   }
 
-  private void readChangesForReplace() throws OrmException {
+  private void readChangesForReplace() {
     Collection<ChangeNotes> allNotes =
         notesFactory.create(
             replaceByChange.values().stream().map(r -> r.ontoChange).collect(toList()));
@@ -2646,10 +2646,9 @@
      *
      * @return whether the new commit is valid
      * @throws IOException
-     * @throws OrmException
      * @throws PermissionBackendException
      */
-    boolean validateNewPatchSet() throws IOException, OrmException, PermissionBackendException {
+    boolean validateNewPatchSet() throws IOException, PermissionBackendException {
       if (!validateNewPatchSetNoteDb()) {
         return false;
       }
@@ -2670,8 +2669,7 @@
       return true;
     }
 
-    boolean validateNewPatchSetForAutoClose()
-        throws IOException, OrmException, PermissionBackendException {
+    boolean validateNewPatchSetForAutoClose() throws IOException, PermissionBackendException {
       if (!validateNewPatchSetNoteDb()) {
         return false;
       }
@@ -2681,8 +2679,7 @@
     }
 
     /** Validates the new PS against permissions and notedb status. */
-    private boolean validateNewPatchSetNoteDb()
-        throws IOException, OrmException, PermissionBackendException {
+    private boolean validateNewPatchSetNoteDb() throws IOException, PermissionBackendException {
       if (notes == null) {
         reject(inputCommand, "change " + ontoChange + " not found");
         return false;
@@ -2845,7 +2842,7 @@
     }
 
     /** Updates 'this' to add a new patchset. */
-    private void newPatchSet() throws IOException, OrmException {
+    private void newPatchSet() throws IOException {
       RevCommit newCommit = receivePack.getRevWalk().parseCommit(newCommitId);
       psId =
           ChangeUtil.nextPatchSetIdFromAllRefsMap(allRefs(), notes.getChange().currentPatchSetId());
@@ -2909,7 +2906,7 @@
           psId.getParentKey(),
           new BatchUpdateOp() {
             @Override
-            public boolean updateChange(ChangeContext ctx) throws OrmException {
+            public boolean updateChange(ChangeContext ctx) {
               PatchSet ps = psUtil.get(ctx.getNotes(), psId);
               List<String> oldGroups = ps.getGroups();
               if (oldGroups == null) {
@@ -3217,7 +3214,7 @@
                   "Auto-closing %s changes with existing patch sets and %s with new patch sets",
                   existingPatchSets, newPatchSets);
               bu.execute();
-            } catch (IOException | OrmException | PermissionBackendException e) {
+            } catch (IOException | StorageException | PermissionBackendException e) {
               logger.atSevere().withCause(e).log("Failed to auto-close changes");
               return null;
             }
@@ -3241,7 +3238,7 @@
     }
   }
 
-  private Optional<ChangeNotes> getChangeNotes(Change.Id changeId) throws OrmException {
+  private Optional<ChangeNotes> getChangeNotes(Change.Id changeId) {
     try {
       return Optional.of(notesFactory.createChecked(project.getNameKey(), changeId));
     } catch (NoSuchChangeException e) {
@@ -3249,18 +3246,18 @@
     }
   }
 
-  private <T> T executeIndexQuery(Action<T> action) throws OrmException {
+  private <T> T executeIndexQuery(Action<T> action) {
     try {
-      return retryHelper.execute(ActionType.INDEX_QUERY, action, OrmException.class::isInstance);
+      return retryHelper.execute(
+          ActionType.INDEX_QUERY, action, StorageException.class::isInstance);
     } catch (Exception e) {
       Throwables.throwIfUnchecked(e);
-      Throwables.throwIfInstanceOf(e, OrmException.class);
-      throw new OrmException(e);
+      Throwables.throwIfInstanceOf(e, StorageException.class);
+      throw new StorageException(e);
     }
   }
 
-  private Map<Change.Key, ChangeNotes> openChangesByKeyByBranch(Branch.NameKey branch)
-      throws OrmException {
+  private Map<Change.Key, ChangeNotes> openChangesByKeyByBranch(Branch.NameKey branch) {
     Map<Change.Key, ChangeNotes> r = new HashMap<>();
     for (ChangeData cd : queryProvider.get().byBranchOpen(branch)) {
       try {
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java b/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
index 8cbcc88..9aa21af 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
@@ -27,7 +28,6 @@
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
 import com.google.gerrit.server.util.MagicBranch;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Provider;
 import java.util.Collections;
 import java.util.Map;
@@ -118,7 +118,7 @@
         }
       }
       return r;
-    } catch (OrmException err) {
+    } catch (StorageException err) {
       logger.atSevere().withCause(err).log("Cannot list open changes of %s", projectName);
       return Collections.emptySet();
     }
diff --git a/java/com/google/gerrit/server/git/receive/ReplaceOp.java b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
index 6636b1f..b15035a 100644
--- a/java/com/google/gerrit/server/git/receive/ReplaceOp.java
+++ b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
@@ -74,7 +74,6 @@
 import com.google.gerrit.server.update.Context;
 import com.google.gerrit.server.update.RepoContext;
 import com.google.gerrit.server.util.RequestScopePropagator;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.util.Providers;
@@ -242,8 +241,7 @@
 
   @Override
   public boolean updateChange(ChangeContext ctx)
-      throws RestApiException, OrmException, IOException, PermissionBackendException,
-          ConfigInvalidException {
+      throws RestApiException, IOException, PermissionBackendException, ConfigInvalidException {
     notes = ctx.getNotes();
     Change change = notes.getChange();
     if (change == null || change.isClosed()) {
@@ -387,7 +385,7 @@
   }
 
   private ChangeMessage createChangeMessage(ChangeContext ctx, String reviewMessage)
-      throws OrmException, IOException {
+      throws IOException {
     String approvalMessage =
         ApprovalsUtil.renderMessageWithApprovals(
             patchSetId.get(), approvals, scanLabels(ctx, approvals));
@@ -441,7 +439,7 @@
   }
 
   private Map<String, PatchSetApproval> scanLabels(ChangeContext ctx, Map<String, Short> approvals)
-      throws OrmException, IOException {
+      throws IOException {
     Map<String, PatchSetApproval> current = new HashMap<>();
     // We optimize here and only retrieve current when approvals provided
     if (!approvals.isEmpty()) {
@@ -485,8 +483,7 @@
     }
   }
 
-  private List<Comment> publishComments(ChangeContext ctx, boolean workInProgress)
-      throws OrmException {
+  private List<Comment> publishComments(ChangeContext ctx, boolean workInProgress) {
     List<Comment> comments =
         commentsUtil.draftByChangeAuthor(ctx.getNotes(), ctx.getUser().getAccountId());
     publishCommentUtil.publish(
diff --git a/java/com/google/gerrit/server/git/validators/MergeValidators.java b/java/com/google/gerrit/server/git/validators/MergeValidators.java
index 1dd48f1..e7e021b 100644
--- a/java/com/google/gerrit/server/git/validators/MergeValidators.java
+++ b/java/com/google/gerrit/server/git/validators/MergeValidators.java
@@ -17,6 +17,7 @@
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.registration.Extension;
@@ -43,7 +44,6 @@
 import com.google.gerrit.server.project.ProjectConfig;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.List;
@@ -301,7 +301,7 @@
         if (!cd.currentFilePaths().contains(AccountProperties.ACCOUNT_CONFIG)) {
           return;
         }
-      } catch (IOException | OrmException e) {
+      } catch (StorageException e) {
         logger.atSevere().withCause(e).log("Cannot validate account update");
         throw new MergeValidationException("account validation unavailable");
       }
diff --git a/java/com/google/gerrit/server/group/db/GroupConfig.java b/java/com/google/gerrit/server/group/db/GroupConfig.java
index abc8c90..903b9c0 100644
--- a/java/com/google/gerrit/server/group/db/GroupConfig.java
+++ b/java/com/google/gerrit/server/group/db/GroupConfig.java
@@ -24,6 +24,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Streams;
+import com.google.gerrit.exceptions.DuplicateKeyException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Project;
@@ -32,7 +33,6 @@
 import com.google.gerrit.server.git.meta.VersionedMetaData;
 import com.google.gerrit.server.group.InternalGroup;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
 import java.io.IOException;
 import java.sql.Timestamp;
 import java.util.Arrays;
@@ -110,11 +110,11 @@
    * @throws IOException if the repository can't be accessed for some reason
    * @throws ConfigInvalidException if a group with the same UUID already exists but can't be read
    *     due to an invalid format
-   * @throws OrmDuplicateKeyException if a group with the same UUID already exists
+   * @throws DuplicateKeyException if a group with the same UUID already exists
    */
   public static GroupConfig createForNewGroup(
       Project.NameKey projectName, Repository repository, InternalGroupCreation groupCreation)
-      throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
+      throws IOException, ConfigInvalidException, DuplicateKeyException {
     GroupConfig groupConfig = new GroupConfig(groupCreation.getGroupUUID());
     groupConfig.load(projectName, repository);
     groupConfig.setGroupCreation(groupCreation);
@@ -241,11 +241,10 @@
     this.allowSaveEmptyName = true;
   }
 
-  private void setGroupCreation(InternalGroupCreation groupCreation)
-      throws OrmDuplicateKeyException {
+  private void setGroupCreation(InternalGroupCreation groupCreation) throws DuplicateKeyException {
     checkLoaded();
     if (loadedGroup.isPresent()) {
-      throw new OrmDuplicateKeyException(String.format("Group %s already exists", groupUuid.get()));
+      throw new DuplicateKeyException(String.format("Group %s already exists", groupUuid.get()));
     }
 
     this.groupCreation = Optional.of(groupCreation);
diff --git a/java/com/google/gerrit/server/group/db/GroupNameNotes.java b/java/com/google/gerrit/server/group/db/GroupNameNotes.java
index e95e91f..eda7153 100644
--- a/java/com/google/gerrit/server/group/db/GroupNameNotes.java
+++ b/java/com/google/gerrit/server/group/db/GroupNameNotes.java
@@ -29,11 +29,11 @@
 import com.google.common.hash.Hashing;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.exceptions.DuplicateKeyException;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.git.meta.VersionedMetaData;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Map;
@@ -114,7 +114,7 @@
    * @throws IOException if the repository can't be accessed for some reason
    * @throws ConfigInvalidException if the note for the specified group doesn't exist or is in an
    *     invalid state
-   * @throws OrmDuplicateKeyException if a group with the new name already exists
+   * @throws DuplicateKeyException if a group with the new name already exists
    */
   public static GroupNameNotes forRename(
       Project.NameKey projectName,
@@ -122,7 +122,7 @@
       AccountGroup.UUID groupUuid,
       AccountGroup.NameKey oldName,
       AccountGroup.NameKey newName)
-      throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
+      throws IOException, ConfigInvalidException, DuplicateKeyException {
     requireNonNull(oldName);
     requireNonNull(newName);
 
@@ -146,14 +146,14 @@
    * @return an instance of {@code GroupNameNotes} configured for a specific group creation
    * @throws IOException if the repository can't be accessed for some reason
    * @throws ConfigInvalidException in no case so far
-   * @throws OrmDuplicateKeyException if a group with the new name already exists
+   * @throws DuplicateKeyException if a group with the new name already exists
    */
   public static GroupNameNotes forNewGroup(
       Project.NameKey projectName,
       Repository repository,
       AccountGroup.UUID groupUuid,
       AccountGroup.NameKey groupName)
-      throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
+      throws IOException, ConfigInvalidException, DuplicateKeyException {
     requireNonNull(groupName);
 
     GroupNameNotes groupNameNotes = new GroupNameNotes(groupUuid, null, groupName);
@@ -363,9 +363,9 @@
     }
   }
 
-  private void ensureNewNameIsNotUsed() throws OrmDuplicateKeyException {
+  private void ensureNewNameIsNotUsed() throws DuplicateKeyException {
     if (newGroupName.isPresent() && nameConflicting) {
-      throw new OrmDuplicateKeyException(
+      throw new DuplicateKeyException(
           String.format("Name '%s' is already used", newGroupName.get().get()));
     }
   }
diff --git a/java/com/google/gerrit/server/group/db/GroupsUpdate.java b/java/com/google/gerrit/server/group/db/GroupsUpdate.java
index 194258e..710f886 100644
--- a/java/com/google/gerrit/server/group/db/GroupsUpdate.java
+++ b/java/com/google/gerrit/server/group/db/GroupsUpdate.java
@@ -21,7 +21,8 @@
 import com.google.common.collect.Sets;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.git.LockFailureException;
 import com.google.gerrit.git.RefUpdateUtil;
 import com.google.gerrit.reviewdb.client.Account;
@@ -45,7 +46,6 @@
 import com.google.gerrit.server.logging.TraceContext.TraceTimer;
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
@@ -256,13 +256,13 @@
    * @param groupUpdate an {@code InternalGroupUpdate} which specifies optional properties of the
    *     group. If this {@code InternalGroupUpdate} updates a property which was already specified
    *     by the {@code InternalGroupCreation}, the value of this {@code InternalGroupUpdate} wins.
-   * @throws OrmDuplicateKeyException if a group with the chosen name already exists
+   * @throws DuplicateKeyException if a group with the chosen name already exists
    * @throws IOException if indexing fails, or an error occurs while reading/writing from/to NoteDb
    * @return the created {@code InternalGroup}
    */
   public InternalGroup createGroup(
       InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
-      throws OrmDuplicateKeyException, IOException, ConfigInvalidException {
+      throws DuplicateKeyException, IOException, ConfigInvalidException {
     try (TraceTimer timer =
         TraceContext.newTimer(
             "Creating group '%s'", groupUpdate.getName().orElseGet(groupCreation::getNameKey))) {
@@ -279,12 +279,12 @@
    * @param groupUuid the UUID of the group to update
    * @param groupUpdate an {@code InternalGroupUpdate} which indicates the desired updates on the
    *     group
-   * @throws OrmDuplicateKeyException if the new name of the group is used by another group
+   * @throws DuplicateKeyException if the new name of the group is used by another group
    * @throws IOException if indexing fails, or an error occurs while reading/writing from/to NoteDb
    * @throws NoSuchGroupException if the specified group doesn't exist
    */
   public void updateGroup(AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
-      throws OrmDuplicateKeyException, IOException, NoSuchGroupException, ConfigInvalidException {
+      throws DuplicateKeyException, IOException, NoSuchGroupException, ConfigInvalidException {
     try (TraceTimer timer = TraceContext.newTimer("Updating group %s", groupUuid)) {
       Optional<Timestamp> updatedOn = groupUpdate.getUpdatedOn();
       if (!updatedOn.isPresent()) {
@@ -301,7 +301,7 @@
 
   private InternalGroup createGroupInNoteDbWithRetry(
       InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
-      throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
+      throws IOException, ConfigInvalidException, DuplicateKeyException {
     try {
       return retryHelper.execute(
           RetryHelper.ActionType.GROUP_UPDATE,
@@ -311,7 +311,7 @@
       Throwables.throwIfUnchecked(e);
       Throwables.throwIfInstanceOf(e, IOException.class);
       Throwables.throwIfInstanceOf(e, ConfigInvalidException.class);
-      Throwables.throwIfInstanceOf(e, OrmDuplicateKeyException.class);
+      Throwables.throwIfInstanceOf(e, DuplicateKeyException.class);
       throw new IOException(e);
     }
   }
@@ -319,7 +319,7 @@
   @VisibleForTesting
   public InternalGroup createGroupInNoteDb(
       InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
-      throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
+      throws IOException, ConfigInvalidException, DuplicateKeyException {
     try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
       AccountGroup.NameKey groupName = groupUpdate.getName().orElseGet(groupCreation::getNameKey);
       GroupNameNotes groupNameNotes =
@@ -341,7 +341,7 @@
 
   private UpdateResult updateGroupInNoteDbWithRetry(
       AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
-      throws IOException, ConfigInvalidException, OrmDuplicateKeyException, NoSuchGroupException {
+      throws IOException, ConfigInvalidException, DuplicateKeyException, NoSuchGroupException {
     try {
       return retryHelper.execute(
           RetryHelper.ActionType.GROUP_UPDATE,
@@ -351,7 +351,7 @@
       Throwables.throwIfUnchecked(e);
       Throwables.throwIfInstanceOf(e, IOException.class);
       Throwables.throwIfInstanceOf(e, ConfigInvalidException.class);
-      Throwables.throwIfInstanceOf(e, OrmDuplicateKeyException.class);
+      Throwables.throwIfInstanceOf(e, DuplicateKeyException.class);
       Throwables.throwIfInstanceOf(e, NoSuchGroupException.class);
       throw new IOException(e);
     }
@@ -360,7 +360,7 @@
   @VisibleForTesting
   public UpdateResult updateGroupInNoteDb(
       AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
-      throws IOException, ConfigInvalidException, OrmDuplicateKeyException, NoSuchGroupException {
+      throws IOException, ConfigInvalidException, DuplicateKeyException, NoSuchGroupException {
     try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
       GroupConfig groupConfig = GroupConfig.loadForGroup(allUsersName, allUsersRepo, groupUuid);
       groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
diff --git a/java/com/google/gerrit/server/index/IndexUtils.java b/java/com/google/gerrit/server/index/IndexUtils.java
index e7bdfea..4b5cd49 100644
--- a/java/com/google/gerrit/server/index/IndexUtils.java
+++ b/java/com/google/gerrit/server/index/IndexUtils.java
@@ -18,10 +18,10 @@
 import static com.google.gerrit.server.index.change.ChangeField.LEGACY_ID;
 import static com.google.gerrit.server.index.change.ChangeField.PROJECT;
 
-import com.google.common.base.Function;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.project.ProjectField;
 import com.google.gerrit.server.CurrentUser;
@@ -31,41 +31,28 @@
 import com.google.gerrit.server.query.change.SingleGroupUser;
 import java.io.IOException;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 
 public final class IndexUtils {
   public static final ImmutableMap<String, String> CUSTOM_CHAR_MAPPING =
       ImmutableMap.of("_", " ", ".", " ");
 
-  public static final Function<Exception, IOException> MAPPER =
-      in -> {
-        if (in instanceof IOException) {
-          return (IOException) in;
-        } else if (in instanceof ExecutionException && in.getCause() instanceof IOException) {
-          return (IOException) in.getCause();
-        } else {
-          return new IOException(in);
-        }
-      };
-
-  public static void setReady(SitePaths sitePaths, String name, int version, boolean ready)
-      throws IOException {
+  public static void setReady(SitePaths sitePaths, String name, int version, boolean ready) {
     try {
       GerritIndexStatus cfg = new GerritIndexStatus(sitePaths);
       cfg.setReady(name, version, ready);
       cfg.save();
-    } catch (ConfigInvalidException e) {
-      throw new IOException(e);
+    } catch (ConfigInvalidException | IOException e) {
+      throw new StorageException(e);
     }
   }
 
-  public static boolean getReady(SitePaths sitePaths, String name, int version) throws IOException {
+  public static boolean getReady(SitePaths sitePaths, String name, int version) {
     try {
       GerritIndexStatus cfg = new GerritIndexStatus(sitePaths);
       return cfg.getReady(name, version);
-    } catch (ConfigInvalidException e) {
-      throw new IOException(e);
+    } catch (ConfigInvalidException | IOException e) {
+      throw new StorageException(e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/index/OnlineReindexer.java b/java/com/google/gerrit/server/index/OnlineReindexer.java
index 8a1776d..37677bdd 100644
--- a/java/com/google/gerrit/server/index/OnlineReindexer.java
+++ b/java/com/google/gerrit/server/index/OnlineReindexer.java
@@ -23,7 +23,6 @@
 import com.google.gerrit.index.IndexDefinition;
 import com.google.gerrit.index.SiteIndexer;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
-import java.io.IOException;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -62,7 +61,7 @@
               try {
                 reindex();
                 ok = true;
-              } catch (IOException e) {
+              } catch (RuntimeException e) {
                 logger.atSevere().withCause(e).log(
                     "Online reindex of %s schema version %s failed", name, version(index));
               } finally {
@@ -91,7 +90,7 @@
     return i.getSchema().getVersion();
   }
 
-  private void reindex() throws IOException {
+  private void reindex() {
     listeners.runEach(listener -> listener.onStart(name, oldVersion, newVersion));
     index =
         requireNonNull(
@@ -120,11 +119,7 @@
   public void activateIndex() {
     indexes.setSearchIndex(index);
     logger.atInfo().log("Using %s schema version %s", name, version(index));
-    try {
-      index.markReady(true);
-    } catch (IOException e) {
-      logger.atWarning().log("Error activating new %s schema version %s", name, version(index));
-    }
+    index.markReady(true);
 
     List<I> toRemove = Lists.newArrayListWithExpectedSize(1);
     for (I i : indexes.getWriteIndexes()) {
@@ -133,12 +128,8 @@
       }
     }
     for (I i : toRemove) {
-      try {
-        i.markReady(false);
-        indexes.removeWriteIndex(version(i));
-      } catch (IOException e) {
-        logger.atWarning().log("Error deactivating old %s schema version %s", name, version(i));
-      }
+      i.markReady(false);
+      indexes.removeWriteIndex(version(i));
     }
   }
 }
diff --git a/java/com/google/gerrit/server/index/account/AccountIndexer.java b/java/com/google/gerrit/server/index/account/AccountIndexer.java
index 91fa1d9..7f4f295 100644
--- a/java/com/google/gerrit/server/index/account/AccountIndexer.java
+++ b/java/com/google/gerrit/server/index/account/AccountIndexer.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.index.account;
 
 import com.google.gerrit.reviewdb.client.Account;
-import java.io.IOException;
 
 public interface AccountIndexer {
 
@@ -24,7 +23,7 @@
    *
    * @param id account id to index.
    */
-  void index(Account.Id id) throws IOException;
+  void index(Account.Id id);
 
   /**
    * Synchronously reindex an account if it is stale.
@@ -32,5 +31,5 @@
    * @param id account id to index.
    * @return whether the account was reindexed
    */
-  boolean reindexIfStale(Account.Id id) throws IOException;
+  boolean reindexIfStale(Account.Id id);
 }
diff --git a/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java b/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
index f1b8591..1eaac7a 100644
--- a/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/account/AccountIndexerImpl.java
@@ -17,6 +17,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.events.AccountIndexedListener;
 import com.google.gerrit.index.Index;
 import com.google.gerrit.reviewdb.client.Account;
@@ -74,7 +75,7 @@
   }
 
   @Override
-  public void index(Account.Id id) throws IOException {
+  public void index(Account.Id id) {
     byIdCache.evict(id);
     Optional<AccountState> accountState = byIdCache.get(id);
 
@@ -104,10 +105,14 @@
   }
 
   @Override
-  public boolean reindexIfStale(Account.Id id) throws IOException {
-    if (stalenessChecker.isStale(id)) {
-      index(id);
-      return true;
+  public boolean reindexIfStale(Account.Id id) {
+    try {
+      if (stalenessChecker.isStale(id)) {
+        index(id);
+        return true;
+      }
+    } catch (IOException e) {
+      throw new StorageException(e);
     }
     return false;
   }
diff --git a/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java b/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java
index 18f44f0..8b9fa27 100644
--- a/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java
+++ b/java/com/google/gerrit/server/index/account/IndexedAccountQuery.java
@@ -25,7 +25,6 @@
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.account.AccountState;
-import com.google.gwtorm.server.OrmException;
 
 public class IndexedAccountQuery extends IndexedQuery<Account.Id, AccountState>
     implements DataSource<AccountState>, Matchable<AccountState> {
@@ -37,7 +36,7 @@
   }
 
   @Override
-  public boolean match(AccountState accountState) throws OrmException {
+  public boolean match(AccountState accountState) {
     Predicate<AccountState> pred = getChild(0);
     checkState(
         pred.isMatchable(),
diff --git a/java/com/google/gerrit/server/index/change/ChangeField.java b/java/com/google/gerrit/server/index/change/ChangeField.java
index 50b38d3..394761b 100644
--- a/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -72,8 +72,6 @@
 import com.google.gerrit.server.query.change.ChangeQueryBuilder;
 import com.google.gerrit.server.query.change.ChangeStatusPredicate;
 import com.google.gson.Gson;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
 import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -154,13 +152,8 @@
       exact(ChangeQueryBuilder.FIELD_FILE)
           .buildRepeatable(cd -> firstNonNull(cd.currentFilePaths(), ImmutableList.of()));
 
-  public static Set<String> getFileParts(ChangeData cd) throws OrmException {
-    List<String> paths;
-    try {
-      paths = cd.currentFilePaths();
-    } catch (IOException e) {
-      throw new OrmException(e);
-    }
+  public static Set<String> getFileParts(ChangeData cd) {
+    List<String> paths = cd.currentFilePaths();
 
     Splitter s = Splitter.on('/').omitEmptyStrings();
     Set<String> r = new HashSet<>();
@@ -191,7 +184,7 @@
   public static final FieldDef<ChangeData, Iterable<String>> EXTENSION =
       exact(ChangeQueryBuilder.FIELD_EXTENSION).buildRepeatable(ChangeField::getExtensions);
 
-  public static Set<String> getExtensions(ChangeData cd) throws OrmException {
+  public static Set<String> getExtensions(ChangeData cd) {
     return extensions(cd).collect(toSet());
   }
 
@@ -202,7 +195,7 @@
   public static final FieldDef<ChangeData, String> ONLY_EXTENSIONS =
       exact(ChangeQueryBuilder.FIELD_ONLY_EXTENSIONS).build(ChangeField::getAllExtensionsAsList);
 
-  public static String getAllExtensionsAsList(ChangeData cd) throws OrmException {
+  public static String getAllExtensionsAsList(ChangeData cd) {
     return extensions(cd).distinct().sorted().collect(joining(","));
   }
 
@@ -214,44 +207,31 @@
    * <p>If the change contains multiple files with the same extension the extension is returned
    * multiple times in the stream (once per file).
    */
-  private static Stream<String> extensions(ChangeData cd) throws OrmException {
-    try {
-      return cd.currentFilePaths().stream()
-          // Use case-insensitive file extensions even though other file fields are case-sensitive.
-          // If we want to find "all Java files", we want to match both .java and .JAVA, even if we
-          // normally care about case sensitivity. (Whether we should change the existing file/path
-          // predicates to be case insensitive is a separate question.)
-          .map(f -> Files.getFileExtension(f).toLowerCase(Locale.US));
-    } catch (IOException e) {
-      throw new OrmException(e);
-    }
+  private static Stream<String> extensions(ChangeData cd) {
+    return cd.currentFilePaths().stream()
+        // Use case-insensitive file extensions even though other file fields are case-sensitive.
+        // If we want to find "all Java files", we want to match both .java and .JAVA, even if we
+        // normally care about case sensitivity. (Whether we should change the existing file/path
+        // predicates to be case insensitive is a separate question.)
+        .map(f -> Files.getFileExtension(f).toLowerCase(Locale.US));
   }
 
   /** Footers from the commit message of the current patch set. */
   public static final FieldDef<ChangeData, Iterable<String>> FOOTER =
       exact(ChangeQueryBuilder.FIELD_FOOTER).buildRepeatable(ChangeField::getFooters);
 
-  public static Set<String> getFooters(ChangeData cd) throws OrmException {
-    try {
-      return cd.commitFooters().stream()
-          .map(f -> f.toString().toLowerCase(Locale.US))
-          .collect(toSet());
-    } catch (IOException e) {
-      throw new OrmException(e);
-    }
+  public static Set<String> getFooters(ChangeData cd) {
+    return cd.commitFooters().stream()
+        .map(f -> f.toString().toLowerCase(Locale.US))
+        .collect(toSet());
   }
 
   /** Folders that are touched by the current patch set. */
   public static final FieldDef<ChangeData, Iterable<String>> DIRECTORY =
       exact(ChangeQueryBuilder.FIELD_DIRECTORY).buildRepeatable(ChangeField::getDirectories);
 
-  public static Set<String> getDirectories(ChangeData cd) throws OrmException {
-    List<String> paths;
-    try {
-      paths = cd.currentFilePaths();
-    } catch (IOException e) {
-      throw new OrmException(e);
-    }
+  public static Set<String> getDirectories(ChangeData cd) {
+    List<String> paths = cd.currentFilePaths();
 
     Splitter s = Splitter.on('/').omitEmptyStrings();
     Set<String> r = new HashSet<>();
@@ -470,7 +450,7 @@
   public static final FieldDef<ChangeData, Iterable<String>> EXACT_COMMIT =
       exact(ChangeQueryBuilder.FIELD_EXACTCOMMIT).buildRepeatable(ChangeField::getRevisions);
 
-  private static Set<String> getRevisions(ChangeData cd) throws OrmException {
+  private static Set<String> getRevisions(ChangeData cd) {
     Set<String> revisions = new HashSet<>();
     for (PatchSet ps : cd.patchSets()) {
       if (ps.getRevision() != null) {
@@ -489,7 +469,7 @@
   public static final FieldDef<ChangeData, Iterable<String>> LABEL =
       exact("label2").buildRepeatable(cd -> getLabels(cd, true));
 
-  private static Iterable<String> getLabels(ChangeData cd, boolean owners) throws OrmException {
+  private static Iterable<String> getLabels(ChangeData cd, boolean owners) {
     Set<String> allApprovals = new HashSet<>();
     Set<String> distinctApprovals = new HashSet<>();
     for (PatchSetApproval a : cd.currentApprovals()) {
@@ -506,20 +486,19 @@
     return allApprovals;
   }
 
-  public static Set<String> getAuthorParts(ChangeData cd) throws OrmException, IOException {
+  public static Set<String> getAuthorParts(ChangeData cd) {
     return SchemaUtil.getPersonParts(cd.getAuthor());
   }
 
-  public static Set<String> getAuthorNameAndEmail(ChangeData cd) throws OrmException, IOException {
+  public static Set<String> getAuthorNameAndEmail(ChangeData cd) {
     return getNameAndEmail(cd.getAuthor());
   }
 
-  public static Set<String> getCommitterParts(ChangeData cd) throws OrmException, IOException {
+  public static Set<String> getCommitterParts(ChangeData cd) {
     return SchemaUtil.getPersonParts(cd.getCommitter());
   }
 
-  public static Set<String> getCommitterNameAndEmail(ChangeData cd)
-      throws OrmException, IOException {
+  public static Set<String> getCommitterNameAndEmail(ChangeData cd) {
     return getNameAndEmail(cd.getCommitter());
   }
 
@@ -855,7 +834,7 @@
     return storedSubmitRecords(cd.submitRecords(opts));
   }
 
-  public static List<String> formatSubmitRecordValues(ChangeData cd) throws OrmException {
+  public static List<String> formatSubmitRecordValues(ChangeData cd) {
     return formatSubmitRecordValues(
         cd.submitRecords(SUBMIT_RULE_OPTIONS_STRICT), cd.change().getOwner());
   }
@@ -943,7 +922,7 @@
                 return result;
               });
 
-  private static String getTopic(ChangeData cd) throws OrmException {
+  private static String getTopic(ChangeData cd) {
     Change c = cd.change();
     if (c == null) {
       return null;
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndexer.java b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
index f1120b2..348e0ce 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndexer.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
@@ -27,7 +27,6 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.index.IndexExecutor;
-import com.google.gerrit.server.index.IndexUtils;
 import com.google.gerrit.server.logging.TraceContext;
 import com.google.gerrit.server.logging.TraceContext.TraceTimer;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
@@ -37,7 +36,6 @@
 import com.google.inject.OutOfScopeException;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -62,16 +60,6 @@
     ChangeIndexer create(ListeningExecutorService executor, ChangeIndexCollection indexes);
   }
 
-  @SuppressWarnings("deprecation")
-  public static com.google.common.util.concurrent.CheckedFuture<?, IOException> allAsList(
-      List<? extends ListenableFuture<?>> futures) {
-    // allAsList propagates the first seen exception, wrapped in
-    // ExecutionException, so we can reuse the same mapper as for a single
-    // future. Assume the actual contents of the exception are not useful to
-    // callers. All exceptions are already logged by IndexTask.
-    return Futures.makeChecked(Futures.allAsList(futures), IndexUtils.MAPPER);
-  }
-
   @Nullable private final ChangeIndexCollection indexes;
   @Nullable private final ChangeIndex index;
   private final ChangeData.Factory changeDataFactory;
@@ -134,9 +122,7 @@
    * @param id change to index.
    * @return future for the indexing task.
    */
-  @SuppressWarnings("deprecation")
-  public com.google.common.util.concurrent.CheckedFuture<?, IOException> indexAsync(
-      Project.NameKey project, Change.Id id) {
+  public ListenableFuture<?> indexAsync(Project.NameKey project, Change.Id id) {
     return submit(new IndexTask(project, id));
   }
 
@@ -146,14 +132,12 @@
    * @param ids changes to index.
    * @return future for completing indexing of all changes.
    */
-  @SuppressWarnings("deprecation")
-  public com.google.common.util.concurrent.CheckedFuture<?, IOException> indexAsync(
-      Project.NameKey project, Collection<Change.Id> ids) {
+  public ListenableFuture<?> indexAsync(Project.NameKey project, Collection<Change.Id> ids) {
     List<ListenableFuture<?>> futures = new ArrayList<>(ids.size());
     for (Change.Id id : ids) {
       futures.add(indexAsync(project, id));
     }
-    return allAsList(futures);
+    return Futures.allAsList(futures);
   }
 
   /**
@@ -161,7 +145,7 @@
    *
    * @param cd change to index.
    */
-  public void index(ChangeData cd) throws IOException {
+  public void index(ChangeData cd) {
     indexImpl(cd);
 
     // Always double-check whether the change might be stale immediately after
@@ -185,7 +169,7 @@
     autoReindexIfStale(cd);
   }
 
-  private void indexImpl(ChangeData cd) throws IOException {
+  private void indexImpl(ChangeData cd) {
     logger.atFine().log("Replace change %d in index.", cd.getId().get());
     for (Index<?, ChangeData> i : getWriteIndexes()) {
       try (TraceTimer traceTimer =
@@ -211,7 +195,7 @@
    *
    * @param change change to index.
    */
-  public void index(Change change) throws IOException {
+  public void index(Change change) {
     index(changeDataFactory.create(change));
   }
 
@@ -221,7 +205,7 @@
    * @param project the project to which the change belongs.
    * @param changeId ID of the change to index.
    */
-  public void index(Project.NameKey project, Change.Id changeId) throws IOException {
+  public void index(Project.NameKey project, Change.Id changeId) {
     index(changeDataFactory.create(project, changeId));
   }
 
@@ -231,8 +215,7 @@
    * @param id change to delete.
    * @return future for the deleting task.
    */
-  @SuppressWarnings("deprecation")
-  public com.google.common.util.concurrent.CheckedFuture<?, IOException> deleteAsync(Change.Id id) {
+  public ListenableFuture<?> deleteAsync(Change.Id id) {
     return submit(new DeleteTask(id));
   }
 
@@ -241,7 +224,7 @@
    *
    * @param id change ID to delete.
    */
-  public void delete(Change.Id id) throws IOException {
+  public void delete(Change.Id id) {
     new DeleteTask(id).call();
   }
 
@@ -255,9 +238,7 @@
    * @param id ID of the change to index.
    * @return future for reindexing the change; returns true if the change was stale.
    */
-  @SuppressWarnings("deprecation")
-  public com.google.common.util.concurrent.CheckedFuture<Boolean, IOException> reindexIfStale(
-      Project.NameKey project, Change.Id id) {
+  public ListenableFuture<Boolean> reindexIfStale(Project.NameKey project, Change.Id id) {
     return submit(new ReindexIfStaleTask(project, id), batchExecutor);
   }
 
@@ -277,17 +258,13 @@
     return indexes != null ? indexes.getWriteIndexes() : Collections.singleton(index);
   }
 
-  @SuppressWarnings("deprecation")
-  private <T> com.google.common.util.concurrent.CheckedFuture<T, IOException> submit(
-      Callable<T> task) {
+  private <T> ListenableFuture<T> submit(Callable<T> task) {
     return submit(task, executor);
   }
 
-  @SuppressWarnings("deprecation")
-  private static <T> com.google.common.util.concurrent.CheckedFuture<T, IOException> submit(
+  private static <T> ListenableFuture<T> submit(
       Callable<T> task, ListeningExecutorService executor) {
-    return Futures.makeChecked(
-        Futures.nonCancellationPropagating(executor.submit(task)), IndexUtils.MAPPER);
+    return Futures.nonCancellationPropagating(executor.submit(task));
   }
 
   private abstract class AbstractIndexTask<T> implements Callable<T> {
@@ -351,7 +328,7 @@
     }
 
     @Override
-    public Void call() throws IOException {
+    public Void call() {
       logger.atFine().log("Delete change %d from index.", id.get());
       // Don't bother setting a RequestContext to provide the DB.
       // Implementations should not need to access the DB in order to delete a
diff --git a/java/com/google/gerrit/server/index/change/DummyChangeIndex.java b/java/com/google/gerrit/server/index/change/DummyChangeIndex.java
index f6cee6d..9be93f7 100644
--- a/java/com/google/gerrit/server/index/change/DummyChangeIndex.java
+++ b/java/com/google/gerrit/server/index/change/DummyChangeIndex.java
@@ -20,7 +20,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeDataSource;
-import java.io.IOException;
 
 public class DummyChangeIndex implements ChangeIndex {
   @Override
@@ -32,13 +31,13 @@
   public void close() {}
 
   @Override
-  public void replace(ChangeData cd) throws IOException {}
+  public void replace(ChangeData cd) {}
 
   @Override
-  public void delete(Change.Id id) throws IOException {}
+  public void delete(Change.Id id) {}
 
   @Override
-  public void deleteAll() throws IOException {}
+  public void deleteAll() {}
 
   @Override
   public ChangeDataSource getSource(Predicate<ChangeData> p, QueryOptions opts) {
@@ -46,7 +45,7 @@
   }
 
   @Override
-  public void markReady(boolean ready) throws IOException {}
+  public void markReady(boolean ready) {}
 
   public int getMaxLimit() {
     return Integer.MAX_VALUE;
diff --git a/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java b/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
index 8088837..ed09eed 100644
--- a/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
+++ b/java/com/google/gerrit/server/index/change/IndexedChangeQuery.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeDataSource;
-import com.google.gwtorm.server.OrmException;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -81,7 +80,7 @@
   }
 
   @Override
-  public ResultSet<ChangeData> read() throws OrmException {
+  public ResultSet<ChangeData> read() {
     final DataSource<ChangeData> currSource = source;
     final ResultSet<ChangeData> rs = currSource.read();
 
@@ -114,7 +113,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     if (source != null && fromSource.get(cd) == source) {
       return true;
     }
diff --git a/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java b/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
index 669bb1e..4062988 100644
--- a/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
+++ b/java/com/google/gerrit/server/index/change/ReindexAfterRefUpdate.java
@@ -39,7 +39,6 @@
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.gerrit.server.util.OneOffRequestContext;
 import com.google.gerrit.server.util.RequestContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -91,12 +90,8 @@
     if (allUsersName.get().equals(event.getProjectName())) {
       Account.Id accountId = Account.Id.fromRef(event.getRefName());
       if (accountId != null && !event.getRefName().startsWith(RefNames.REFS_STARRED_CHANGES)) {
-        try {
-          accountCache.evict(accountId);
-          indexer.get().index(accountId);
-        } catch (IOException e) {
-          logger.atSevere().withCause(e).log("Reindex account %s failed.", accountId);
-        }
+        accountCache.evict(accountId);
+        indexer.get().index(accountId);
       }
     }
 
@@ -152,7 +147,7 @@
     }
 
     @Override
-    protected List<Change> impl(RequestContext ctx) throws OrmException {
+    protected List<Change> impl(RequestContext ctx) {
       String ref = event.getRefName();
       Project.NameKey project = new Project.NameKey(event.getProjectName());
       if (ref.equals(RefNames.REFS_CONFIG)) {
@@ -179,7 +174,7 @@
     }
 
     @Override
-    protected Void impl(RequestContext ctx) throws OrmException, IOException {
+    protected Void impl(RequestContext ctx) throws IOException {
       // Reload change, as some time may have passed since GetChanges.
       try {
         Change c =
diff --git a/java/com/google/gerrit/server/index/group/GroupIndexer.java b/java/com/google/gerrit/server/index/group/GroupIndexer.java
index 503fd6b..5d9232e 100644
--- a/java/com/google/gerrit/server/index/group/GroupIndexer.java
+++ b/java/com/google/gerrit/server/index/group/GroupIndexer.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.index.group;
 
 import com.google.gerrit.reviewdb.client.AccountGroup;
-import java.io.IOException;
 
 public interface GroupIndexer {
 
@@ -24,7 +23,7 @@
    *
    * @param uuid group UUID to index.
    */
-  void index(AccountGroup.UUID uuid) throws IOException;
+  void index(AccountGroup.UUID uuid);
 
   /**
    * Synchronously reindex a group if it is stale.
@@ -32,5 +31,5 @@
    * @param uuid group UUID to index.
    * @return whether the group was reindexed
    */
-  boolean reindexIfStale(AccountGroup.UUID uuid) throws IOException;
+  boolean reindexIfStale(AccountGroup.UUID uuid);
 }
diff --git a/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java b/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
index a9124e1..5982de7 100644
--- a/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/group/GroupIndexerImpl.java
@@ -17,6 +17,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.events.GroupIndexedListener;
 import com.google.gerrit.index.Index;
 import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -74,7 +75,7 @@
   }
 
   @Override
-  public void index(AccountGroup.UUID uuid) throws IOException {
+  public void index(AccountGroup.UUID uuid) {
     // Evict the cache to get an up-to-date value for sure.
     groupCache.evict(uuid);
     Optional<InternalGroup> internalGroup = groupCache.get(uuid);
@@ -104,10 +105,14 @@
   }
 
   @Override
-  public boolean reindexIfStale(AccountGroup.UUID uuid) throws IOException {
-    if (stalenessChecker.isStale(uuid)) {
-      index(uuid);
-      return true;
+  public boolean reindexIfStale(AccountGroup.UUID uuid) {
+    try {
+      if (stalenessChecker.isStale(uuid)) {
+        index(uuid);
+        return true;
+      }
+    } catch (IOException e) {
+      throw new StorageException(e);
     }
     return false;
   }
diff --git a/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java b/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
index 1a74eca..6d6f78d 100644
--- a/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
+++ b/java/com/google/gerrit/server/index/project/ProjectIndexerImpl.java
@@ -30,7 +30,6 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
-import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
 
@@ -71,7 +70,7 @@
   }
 
   @Override
-  public void index(Project.NameKey nameKey) throws IOException {
+  public void index(Project.NameKey nameKey) {
     ProjectState projectState = projectCache.get(nameKey);
     if (projectState != null) {
       logger.atFine().log("Replace project %s in index", nameKey.get());
diff --git a/java/com/google/gerrit/server/mail/MailUtil.java b/java/com/google/gerrit/server/mail/MailUtil.java
index 3a5a2cf..4afcc7b 100644
--- a/java/com/google/gerrit/server/mail/MailUtil.java
+++ b/java/com/google/gerrit/server/mail/MailUtil.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.ReviewerSet;
 import com.google.gerrit.server.account.AccountResolver;
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.HashSet;
@@ -37,7 +36,7 @@
 
   public static MailRecipients getRecipientsFromFooters(
       AccountResolver accountResolver, List<FooterLine> footerLines)
-      throws OrmException, IOException, ConfigInvalidException {
+      throws IOException, ConfigInvalidException {
     MailRecipients recipients = new MailRecipients();
     for (FooterLine footerLine : footerLines) {
       try {
@@ -62,7 +61,7 @@
 
   @SuppressWarnings("deprecation")
   private static Account.Id toAccountId(AccountResolver accountResolver, String nameOrEmail)
-      throws OrmException, UnprocessableEntityException, IOException, ConfigInvalidException {
+      throws UnprocessableEntityException, IOException, ConfigInvalidException {
     return accountResolver.resolveByNameOrEmail(nameOrEmail).asUnique().getAccount().getId();
   }
 
diff --git a/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
index 39cd9c3..b1acab9 100644
--- a/java/com/google/gerrit/server/mail/receive/MailProcessor.java
+++ b/java/com/google/gerrit/server/mail/receive/MailProcessor.java
@@ -19,6 +19,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.Side;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.registration.DynamicMap;
@@ -64,7 +65,6 @@
 import com.google.gerrit.server.util.ManualRequestContext;
 import com.google.gerrit.server.util.OneOffRequestContext;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -147,7 +147,7 @@
   }
 
   private void processImpl(BatchUpdate.Factory buf, MailMessage message)
-      throws OrmException, UpdateException, RestApiException, IOException {
+      throws UpdateException, RestApiException, IOException {
     for (Extension<MailFilter> filter : mailFilters) {
       if (!filter.getProvider().get().shouldProcessMessage(message)) {
         logger.atWarning().log(
@@ -208,7 +208,7 @@
 
   private void persistComments(
       BatchUpdate.Factory buf, MailMessage message, MailMetadata metadata, Account.Id sender)
-      throws OrmException, UpdateException, RestApiException {
+      throws UpdateException, RestApiException {
     try (ManualRequestContext ctx = oneOffRequestContext.openAs(sender)) {
       List<ChangeData> changeDataList =
           queryProvider.get().byLegacyChangeId(new Change.Id(metadata.changeNumber));
@@ -283,11 +283,11 @@
 
     @Override
     public boolean updateChange(ChangeContext ctx)
-        throws OrmException, UnprocessableEntityException, PatchListNotAvailableException {
+        throws UnprocessableEntityException, PatchListNotAvailableException {
       patchSet = psUtil.get(ctx.getNotes(), psId);
       notes = ctx.getNotes();
       if (patchSet == null) {
-        throw new OrmException("patch set not found: " + psId);
+        throw new StorageException("patch set not found: " + psId);
       }
 
       changeMessage = generateChangeMessage(ctx);
@@ -358,7 +358,7 @@
     }
 
     private PatchSet targetPatchSetForComment(
-        ChangeContext ctx, MailComment mailComment, PatchSet current) throws OrmException {
+        ChangeContext ctx, MailComment mailComment, PatchSet current) {
       if (mailComment.getInReplyTo() != null) {
         return psUtil.get(
             ctx.getNotes(),
@@ -369,7 +369,7 @@
 
     private Comment persistentCommentFromMailComment(
         ChangeContext ctx, MailComment mailComment, PatchSet patchSetForComment)
-        throws OrmException, UnprocessableEntityException, PatchListNotAvailableException {
+        throws UnprocessableEntityException, PatchListNotAvailableException {
       String fileName;
       // The patch set that this comment is based on is different if this
       // comment was sent in reply to a comment on a previous patch set.
@@ -412,7 +412,7 @@
     return "(" + numComments + (numComments > 1 ? " comments)" : " comment)");
   }
 
-  private Set<String> existingMessageIds(ChangeData cd) throws OrmException {
+  private Set<String> existingMessageIds(ChangeData cd) {
     Set<String> existingMessageIds = new HashSet<>();
     cd.messages().stream()
         .forEach(
diff --git a/java/com/google/gerrit/server/mail/send/AbandonedSender.java b/java/com/google/gerrit/server/mail/send/AbandonedSender.java
index 05dd542..1017965 100644
--- a/java/com/google/gerrit/server/mail/send/AbandonedSender.java
+++ b/java/com/google/gerrit/server/mail/send/AbandonedSender.java
@@ -14,11 +14,10 @@
 
 package com.google.gerrit.server.mail.send;
 
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.account.ProjectWatches.NotifyType;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
@@ -31,8 +30,7 @@
 
   @Inject
   public AbandonedSender(
-      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
-      throws OrmException {
+      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
     super(ea, "abandon", ChangeEmail.newChangeData(ea, project, id));
   }
 
diff --git a/java/com/google/gerrit/server/mail/send/AddKeySender.java b/java/com/google/gerrit/server/mail/send/AddKeySender.java
index 433bd9b..da866f4 100644
--- a/java/com/google/gerrit/server/mail/send/AddKeySender.java
+++ b/java/com/google/gerrit/server/mail/send/AddKeySender.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.common.base.Joiner;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.mail.Address;
diff --git a/java/com/google/gerrit/server/mail/send/AddReviewerSender.java b/java/com/google/gerrit/server/mail/send/AddReviewerSender.java
index cb70106..22abd9c 100644
--- a/java/com/google/gerrit/server/mail/send/AddReviewerSender.java
+++ b/java/com/google/gerrit/server/mail/send/AddReviewerSender.java
@@ -14,10 +14,9 @@
 
 package com.google.gerrit.server.mail.send;
 
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
@@ -29,8 +28,7 @@
 
   @Inject
   public AddReviewerSender(
-      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
-      throws OrmException {
+      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
     super(ea, newChangeData(ea, project, id));
   }
 
diff --git a/java/com/google/gerrit/server/mail/send/ChangeEmail.java b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
index 012d212..4905823 100644
--- a/java/com/google/gerrit/server/mail/send/ChangeEmail.java
+++ b/java/com/google/gerrit/server/mail/send/ChangeEmail.java
@@ -18,7 +18,8 @@
 import com.google.common.collect.ListMultimap;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -43,7 +44,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.template.soy.data.SoyListData;
 import com.google.template.soy.data.SoyMapData;
 import java.io.IOException;
@@ -84,7 +84,7 @@
   protected Set<Account.Id> authors;
   protected boolean emailOnlyAuthors;
 
-  protected ChangeEmail(EmailArguments ea, String mc, ChangeData cd) throws OrmException {
+  protected ChangeEmail(EmailArguments ea, String mc, ChangeData cd) {
     super(ea, mc, cd.change().getDest());
     changeData = cd;
     change = cd.change();
@@ -150,7 +150,7 @@
     if (patchSet == null) {
       try {
         patchSet = changeData.currentPatchSet();
-      } catch (OrmException err) {
+      } catch (StorageException err) {
         patchSet = null;
       }
     }
@@ -160,7 +160,7 @@
       if (patchSetInfo == null) {
         try {
           patchSetInfo = args.patchSetInfoFactory.get(changeData.notes(), patchSet.getId());
-        } catch (PatchSetInfoNotAvailableException | OrmException err) {
+        } catch (PatchSetInfoNotAvailableException | StorageException err) {
           patchSetInfo = null;
         }
       }
@@ -169,7 +169,7 @@
 
     try {
       stars = changeData.stars();
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       throw new EmailException("Failed to load stars for change " + change.getChangeId(), e);
     }
 
@@ -190,7 +190,7 @@
         addByEmail(
             RecipientType.CC,
             changeData.reviewersByEmail().byState(ReviewerStateInternal.REVIEWER));
-      } catch (OrmException e) {
+      } catch (StorageException e) {
         throw new EmailException("Failed to add unregistered CCs " + change.getChangeId(), e);
       }
     }
@@ -294,7 +294,7 @@
     } else {
       try {
         ps = args.patchSetUtil.get(changeData.notes(), new PatchSet.Id(change.getId(), patchSetId));
-      } catch (OrmException e) {
+      } catch (StorageException e) {
         throw new PatchListNotAvailableException("Failed to get patchSet");
       }
     }
@@ -343,8 +343,7 @@
   }
 
   @Override
-  protected final Watchers getWatchers(NotifyType type, boolean includeWatchersFromNotifyConfig)
-      throws OrmException {
+  protected final Watchers getWatchers(NotifyType type, boolean includeWatchersFromNotifyConfig) {
     if (!NotifyHandling.ALL.equals(notify.handling())) {
       return new Watchers();
     }
@@ -364,7 +363,7 @@
       for (Account.Id id : changeData.reviewers().all()) {
         add(RecipientType.CC, id);
       }
-    } catch (OrmException err) {
+    } catch (StorageException err) {
       logger.atWarning().withCause(err).log("Cannot CC users that reviewed updated change");
     }
   }
@@ -380,7 +379,7 @@
       for (Account.Id id : changeData.reviewers().byState(ReviewerStateInternal.REVIEWER)) {
         add(RecipientType.CC, id);
       }
-    } catch (OrmException err) {
+    } catch (StorageException err) {
       logger.atWarning().withCause(err).log("Cannot CC users that commented on updated change");
     }
   }
@@ -506,7 +505,7 @@
       for (Account.Id who : changeData.reviewers().byState(state)) {
         reviewers.add(getNameEmailFor(who));
       }
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atWarning().withCause(e).log("Cannot get change reviewers");
     }
     return reviewers;
diff --git a/java/com/google/gerrit/server/mail/send/CommentSender.java b/java/com/google/gerrit/server/mail/send/CommentSender.java
index 0f55366..01d8a17 100644
--- a/java/com/google/gerrit/server/mail/send/CommentSender.java
+++ b/java/com/google/gerrit/server/mail/send/CommentSender.java
@@ -20,8 +20,9 @@
 import com.google.common.collect.Ordering;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.FilenameComparator;
-import com.google.gerrit.common.errors.EmailException;
-import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.exceptions.EmailException;
+import com.google.gerrit.exceptions.NoSuchEntityException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.mail.MailHeader;
 import com.google.gerrit.mail.MailProcessingUtil;
@@ -41,7 +42,6 @@
 import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
 import com.google.gerrit.server.util.LabelVote;
 import com.google.gwtorm.client.KeyUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
@@ -119,8 +119,7 @@
       CommentsUtil commentsUtil,
       @GerritServerConfig Config cfg,
       @Assisted Project.NameKey project,
-      @Assisted Change.Id id)
-      throws OrmException {
+      @Assisted Change.Id id) {
     super(ea, "comment", newChangeData(ea, project, id));
     this.commentsUtil = commentsUtil;
     this.incomingEmailEnabled =
@@ -129,7 +128,7 @@
     this.replyToAddress = cfg.getString("sendemail", null, "replyToAddress");
   }
 
-  public void setComments(List<Comment> comments) throws OrmException {
+  public void setComments(List<Comment> comments) {
     inlineComments = comments;
 
     Set<String> paths = new HashSet<>();
@@ -316,7 +315,7 @@
     Comment.Key key = new Comment.Key(child.parentUuid, child.key.filename, child.key.patchSetId);
     try {
       return commentsUtil.getPublished(changeData.notes(), key);
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atWarning().log("Could not find the parent of this comment: %s", child);
       return Optional.empty();
     }
diff --git a/java/com/google/gerrit/server/mail/send/CreateChangeSender.java b/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
index fc9c14a..9895e07 100644
--- a/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
+++ b/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
@@ -15,7 +15,8 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
@@ -24,7 +25,6 @@
 import com.google.gerrit.server.mail.send.ProjectWatch.Watchers;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.RefPermission;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.util.stream.StreamSupport;
@@ -44,8 +44,7 @@
       EmailArguments ea,
       PermissionBackend permissionBackend,
       @Assisted Project.NameKey project,
-      @Assisted Change.Id id)
-      throws OrmException {
+      @Assisted Change.Id id) {
     super(ea, newChangeData(ea, project, id));
     this.permissionBackend = permissionBackend;
   }
@@ -66,7 +65,7 @@
       add(RecipientType.TO, matching.to);
       add(RecipientType.CC, matching.cc);
       add(RecipientType.BCC, matching.bcc);
-    } catch (OrmException err) {
+    } catch (StorageException err) {
       // Just don't CC everyone. Better to send a partial message to those
       // we already have queued up then to fail deliver entirely to people
       // who have a lower interest in the change.
diff --git a/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java b/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
index 576d506..f941acc 100644
--- a/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
+++ b/java/com/google/gerrit/server/mail/send/DeleteReviewerSender.java
@@ -14,14 +14,13 @@
 
 package com.google.gerrit.server.mail.send;
 
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.account.ProjectWatches.NotifyType;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.util.ArrayList;
@@ -42,8 +41,7 @@
 
   @Inject
   public DeleteReviewerSender(
-      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
-      throws OrmException {
+      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
     super(ea, "deleteReviewer", newChangeData(ea, project, id));
   }
 
diff --git a/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java b/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
index 0c81293..195d53d 100644
--- a/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
+++ b/java/com/google/gerrit/server/mail/send/DeleteVoteSender.java
@@ -14,11 +14,10 @@
 
 package com.google.gerrit.server.mail.send;
 
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.account.ProjectWatches.NotifyType;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
@@ -31,8 +30,7 @@
 
   @Inject
   protected DeleteVoteSender(
-      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
-      throws OrmException {
+      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
     super(ea, "deleteVote", newChangeData(ea, project, id));
   }
 
diff --git a/java/com/google/gerrit/server/mail/send/EmailSender.java b/java/com/google/gerrit/server/mail/send/EmailSender.java
index ce4964d..9b3a1f7 100644
--- a/java/com/google/gerrit/server/mail/send/EmailSender.java
+++ b/java/com/google/gerrit/server/mail/send/EmailSender.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.mail.EmailHeader;
 import java.util.Collection;
diff --git a/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java b/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
index 99edc04..b5d384d 100644
--- a/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
+++ b/java/com/google/gerrit/server/mail/send/InboundEmailRejectionSender.java
@@ -16,7 +16,7 @@
 
 import static java.util.Objects.requireNonNull;
 
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.mail.MailHeader;
diff --git a/java/com/google/gerrit/server/mail/send/MergedSender.java b/java/com/google/gerrit/server/mail/send/MergedSender.java
index b524c83..cbc4117 100644
--- a/java/com/google/gerrit/server/mail/send/MergedSender.java
+++ b/java/com/google/gerrit/server/mail/send/MergedSender.java
@@ -19,13 +19,13 @@
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelTypes;
 import com.google.gerrit.common.data.LabelValue;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.account.ProjectWatches.NotifyType;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
@@ -38,8 +38,8 @@
   private final LabelTypes labelTypes;
 
   @Inject
-  public MergedSender(EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
-      throws OrmException {
+  public MergedSender(
+      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
     super(ea, "merged", newChangeData(ea, project, id));
     labelTypes = changeData.getLabelTypes();
   }
@@ -82,7 +82,7 @@
       }
 
       return format("Approvals", pos) + format("Objections", neg);
-    } catch (OrmException err) {
+    } catch (StorageException err) {
       // Don't list the approvals
     }
     return "";
diff --git a/java/com/google/gerrit/server/mail/send/NewChangeSender.java b/java/com/google/gerrit/server/mail/send/NewChangeSender.java
index c45da40..7a4fede 100644
--- a/java/com/google/gerrit/server/mail/send/NewChangeSender.java
+++ b/java/com/google/gerrit/server/mail/send/NewChangeSender.java
@@ -14,12 +14,11 @@
 
 package com.google.gerrit.server.mail.send;
 
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
@@ -33,7 +32,7 @@
   private final Set<Account.Id> extraCC = new HashSet<>();
   private final Set<Address> extraCCByEmail = new HashSet<>();
 
-  protected NewChangeSender(EmailArguments ea, ChangeData cd) throws OrmException {
+  protected NewChangeSender(EmailArguments ea, ChangeData cd) {
     super(ea, "newchange", cd);
   }
 
diff --git a/java/com/google/gerrit/server/mail/send/NotificationEmail.java b/java/com/google/gerrit/server/mail/send/NotificationEmail.java
index 032bcbf..64047bf 100644
--- a/java/com/google/gerrit/server/mail/send/NotificationEmail.java
+++ b/java/com/google/gerrit/server/mail/send/NotificationEmail.java
@@ -17,7 +17,8 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Iterables;
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.mail.MailHeader;
@@ -25,7 +26,6 @@
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.server.account.ProjectWatches.NotifyType;
 import com.google.gerrit.server.mail.send.ProjectWatch.Watchers;
-import com.google.gwtorm.server.OrmException;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -68,7 +68,7 @@
       add(RecipientType.TO, matching.to);
       add(RecipientType.CC, matching.cc);
       add(RecipientType.BCC, matching.bcc);
-    } catch (OrmException err) {
+    } catch (StorageException err) {
       // Just don't CC everyone. Better to send a partial message to those
       // we already have queued up then to fail deliver entirely to people
       // who have a lower interest in the change.
@@ -77,8 +77,7 @@
   }
 
   /** Returns all watchers that are relevant */
-  protected abstract Watchers getWatchers(NotifyType type, boolean includeWatchersFromNotifyConfig)
-      throws OrmException;
+  protected abstract Watchers getWatchers(NotifyType type, boolean includeWatchersFromNotifyConfig);
 
   /** Add users or email addresses to the TO, CC, or BCC list. */
   protected void add(RecipientType type, Watchers.List list) {
diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index a6747b5..db97f06 100644
--- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -19,7 +19,7 @@
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
 import com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailFormat;
diff --git a/java/com/google/gerrit/server/mail/send/ProjectWatch.java b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
index 9a86fdb..fd006c2 100644
--- a/java/com/google/gerrit/server/mail/send/ProjectWatch.java
+++ b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
@@ -35,7 +35,6 @@
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeQueryBuilder;
 import com.google.gerrit.server.query.change.SingleGroupUser;
-import com.google.gwtorm.server.OrmException;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -62,8 +61,7 @@
   }
 
   /** Returns all watchers that are relevant */
-  public final Watchers getWatchers(NotifyType type, boolean includeWatchersFromNotifyConfig)
-      throws OrmException {
+  public final Watchers getWatchers(NotifyType type, boolean includeWatchersFromNotifyConfig) {
     Watchers matching = new Watchers();
     Set<Account.Id> projectWatchers = new HashSet<>();
 
@@ -148,7 +146,7 @@
     }
   }
 
-  private void add(Watchers matching, NotifyConfig nc) throws OrmException, QueryParseException {
+  private void add(Watchers matching, NotifyConfig nc) throws QueryParseException {
     for (GroupReference ref : nc.getGroups()) {
       CurrentUser user = new SingleGroupUser(ref.getUUID());
       if (filterMatch(user, nc.getFilter())) {
@@ -202,8 +200,7 @@
       Account.Id accountId,
       ProjectWatchKey key,
       Set<NotifyType> watchedTypes,
-      NotifyType type)
-      throws OrmException {
+      NotifyType type) {
     IdentifiedUser user = args.identifiedUserFactory.create(accountId);
 
     try {
@@ -221,8 +218,7 @@
     return false;
   }
 
-  private boolean filterMatch(CurrentUser user, String filter)
-      throws OrmException, QueryParseException {
+  private boolean filterMatch(CurrentUser user, String filter) throws QueryParseException {
     ChangeQueryBuilder qb;
     Predicate<ChangeData> p = null;
 
diff --git a/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java b/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java
index 3bca00c..436736b 100644
--- a/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java
+++ b/java/com/google/gerrit/server/mail/send/RegisterNewEmailSender.java
@@ -16,7 +16,7 @@
 
 import static java.util.Objects.requireNonNull;
 
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.IdentifiedUser;
diff --git a/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java b/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
index f2844c4..30bcdeb 100644
--- a/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
+++ b/java/com/google/gerrit/server/mail/send/ReplacePatchSetSender.java
@@ -14,14 +14,13 @@
 
 package com.google.gerrit.server.mail.send;
 
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.account.ProjectWatches.NotifyType;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.util.ArrayList;
@@ -41,8 +40,7 @@
 
   @Inject
   public ReplacePatchSetSender(
-      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
-      throws OrmException {
+      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
     super(ea, "newpatchset", newChangeData(ea, project, id));
   }
 
diff --git a/java/com/google/gerrit/server/mail/send/ReplyToChangeSender.java b/java/com/google/gerrit/server/mail/send/ReplyToChangeSender.java
index 61e9d1d..960c3a8 100644
--- a/java/com/google/gerrit/server/mail/send/ReplyToChangeSender.java
+++ b/java/com/google/gerrit/server/mail/send/ReplyToChangeSender.java
@@ -14,12 +14,11 @@
 
 package com.google.gerrit.server.mail.send;
 
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 
 /** Alert a user to a reply to a change, usually commentary made during review. */
 public abstract class ReplyToChangeSender extends ChangeEmail {
@@ -27,7 +26,7 @@
     T create(Project.NameKey project, Change.Id id);
   }
 
-  protected ReplyToChangeSender(EmailArguments ea, String mc, ChangeData cd) throws OrmException {
+  protected ReplyToChangeSender(EmailArguments ea, String mc, ChangeData cd) {
     super(ea, mc, cd);
   }
 
diff --git a/java/com/google/gerrit/server/mail/send/RestoredSender.java b/java/com/google/gerrit/server/mail/send/RestoredSender.java
index d7f8eb5..0d998aa 100644
--- a/java/com/google/gerrit/server/mail/send/RestoredSender.java
+++ b/java/com/google/gerrit/server/mail/send/RestoredSender.java
@@ -14,11 +14,10 @@
 
 package com.google.gerrit.server.mail.send;
 
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.account.ProjectWatches.NotifyType;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
@@ -31,8 +30,7 @@
 
   @Inject
   public RestoredSender(
-      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
-      throws OrmException {
+      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
     super(ea, "restore", ChangeEmail.newChangeData(ea, project, id));
   }
 
diff --git a/java/com/google/gerrit/server/mail/send/RevertedSender.java b/java/com/google/gerrit/server/mail/send/RevertedSender.java
index 21703a3..48b5d99 100644
--- a/java/com/google/gerrit/server/mail/send/RevertedSender.java
+++ b/java/com/google/gerrit/server/mail/send/RevertedSender.java
@@ -14,11 +14,10 @@
 
 package com.google.gerrit.server.mail.send;
 
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.account.ProjectWatches.NotifyType;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
@@ -30,8 +29,7 @@
 
   @Inject
   public RevertedSender(
-      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id)
-      throws OrmException {
+      EmailArguments ea, @Assisted Project.NameKey project, @Assisted Change.Id id) {
     super(ea, "revert", ChangeEmail.newChangeData(ea, project, id));
   }
 
diff --git a/java/com/google/gerrit/server/mail/send/SetAssigneeSender.java b/java/com/google/gerrit/server/mail/send/SetAssigneeSender.java
index 9708b1b..a120769 100644
--- a/java/com/google/gerrit/server/mail/send/SetAssigneeSender.java
+++ b/java/com/google/gerrit/server/mail/send/SetAssigneeSender.java
@@ -14,12 +14,11 @@
 
 package com.google.gerrit.server.mail.send;
 
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
@@ -35,8 +34,7 @@
       EmailArguments ea,
       @Assisted Project.NameKey project,
       @Assisted Change.Id id,
-      @Assisted Account.Id assignee)
-      throws OrmException {
+      @Assisted Account.Id assignee) {
     super(ea, "setassignee", newChangeData(ea, project, id));
     this.assignee = assignee;
   }
diff --git a/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java b/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
index 617c586..3f103fc 100644
--- a/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
+++ b/java/com/google/gerrit/server/mail/send/SmtpEmailSender.java
@@ -20,7 +20,7 @@
 import com.google.common.primitives.Ints;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.Version;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.mail.EmailHeader;
 import com.google.gerrit.server.config.ConfigUtil;
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
index cecbbe8..1c6057d 100644
--- a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
@@ -20,6 +20,7 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.UsedAt;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.metrics.Timer1;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
@@ -27,7 +28,6 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk;
 import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -131,13 +131,13 @@
     return revision;
   }
 
-  public T load() throws OrmException {
+  public T load() {
     if (loaded) {
       return self();
     }
 
     if (args.failOnLoadForTest.get()) {
-      throw new OrmException("Reading from NoteDb is disabled");
+      throw new StorageException("Reading from NoteDb is disabled");
     }
     try (Timer1.Context timer = args.metrics.readLatency.start(CHANGES);
         Repository repo = args.repoManager.openRepository(getProjectName());
@@ -148,7 +148,7 @@
       onLoad(handle);
       loaded = true;
     } catch (ConfigInvalidException | IOException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
     return self();
   }
@@ -176,12 +176,12 @@
     return new LoadHandle(repo, id);
   }
 
-  public T reload() throws OrmException {
+  public T reload() {
     loaded = false;
     return load();
   }
 
-  public ObjectId loadRevision() throws OrmException {
+  public ObjectId loadRevision() {
     if (loaded) {
       return getRevision();
     }
@@ -189,7 +189,7 @@
       Ref ref = repo.getRefDatabase().exactRef(getRefName());
       return ref != null ? ref.getObjectId() : null;
     } catch (IOException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java b/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
index daac83f..91469f8 100644
--- a/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/AbstractChangeUpdate.java
@@ -27,7 +27,6 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.InternalUser;
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 import java.util.Date;
 import org.eclipse.jgit.lib.CommitBuilder;
@@ -194,11 +193,9 @@
    * @return commit ID produced by inserting this update's commit, or null if this update is a no-op
    *     and should be skipped. The zero ID is a valid return value, and indicates the ref should be
    *     deleted.
-   * @throws OrmException if a Gerrit-level error occurred.
    * @throws IOException if a lower-level error occurred.
    */
-  final ObjectId apply(RevWalk rw, ObjectInserter ins, ObjectId curr)
-      throws OrmException, IOException {
+  final ObjectId apply(RevWalk rw, ObjectInserter ins, ObjectId curr) throws IOException {
     if (isEmpty()) {
       return null;
     }
@@ -246,11 +243,10 @@
    *     indicates to the caller that it should be copied from the parent commit. To indicate that
    *     this update is a no-op (but this could not be determined by {@link #isEmpty()}), return the
    *     sentinel {@link #NO_OP_UPDATE}.
-   * @throws OrmException if a Gerrit-level error occurred.
    * @throws IOException if a lower-level error occurred.
    */
   protected abstract CommitBuilder applyImpl(RevWalk rw, ObjectInserter ins, ObjectId curr)
-      throws OrmException, IOException;
+      throws IOException;
 
   protected static final CommitBuilder NO_OP_UPDATE = new CommitBuilder();
 
diff --git a/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java b/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
index 5a3ebd6..38b7b12 100644
--- a/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeDraftUpdate.java
@@ -19,6 +19,7 @@
 
 import com.google.auto.value.AutoValue;
 import com.google.common.collect.Sets;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Comment;
@@ -28,7 +29,6 @@
 import com.google.gerrit.reviewdb.client.RevId;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.config.AllUsersName;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
 import java.io.IOException;
@@ -132,7 +132,7 @@
 
   private CommitBuilder storeCommentsInNotes(
       RevWalk rw, ObjectInserter ins, ObjectId curr, CommitBuilder cb)
-      throws ConfigInvalidException, OrmException, IOException {
+      throws ConfigInvalidException, IOException {
     RevisionNoteMap<ChangeRevisionNote> rnm = getRevisionNoteMap(rw, curr);
     Set<RevId> updatedRevs = Sets.newHashSetWithExpectedSize(rnm.revisionNotes.size());
     RevisionNoteBuilder.Cache cache = new RevisionNoteBuilder.Cache(rnm);
@@ -184,7 +184,7 @@
   }
 
   private RevisionNoteMap<ChangeRevisionNote> getRevisionNoteMap(RevWalk rw, ObjectId curr)
-      throws ConfigInvalidException, OrmException, IOException {
+      throws ConfigInvalidException, IOException {
     // The old DraftCommentNotes already parsed the revision notes. We can reuse them as long as
     // the ref hasn't advanced.
     ChangeNotes changeNotes = getNotes();
@@ -217,13 +217,13 @@
 
   @Override
   protected CommitBuilder applyImpl(RevWalk rw, ObjectInserter ins, ObjectId curr)
-      throws OrmException, IOException {
+      throws IOException {
     CommitBuilder cb = new CommitBuilder();
     cb.setMessage("Update draft comments");
     try {
       return storeCommentsInNotes(rw, ins, curr, cb);
     } catch (ConfigInvalidException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotes.java b/java/com/google/gerrit/server/notedb/ChangeNotes.java
index af85661..764d41d 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -36,6 +36,7 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
@@ -55,7 +56,6 @@
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -103,17 +103,16 @@
       this.projectCache = projectCache;
     }
 
-    public ChangeNotes createChecked(Change c) throws OrmException {
+    public ChangeNotes createChecked(Change c) {
       return createChecked(c.getProject(), c.getId());
     }
 
-    public ChangeNotes createChecked(Project.NameKey project, Change.Id changeId)
-        throws OrmException {
+    public ChangeNotes createChecked(Project.NameKey project, Change.Id changeId) {
       Change change = newChange(project, changeId);
       return new ChangeNotes(args, change, true, null).load();
     }
 
-    public ChangeNotes createChecked(Change.Id changeId) throws OrmException {
+    public ChangeNotes createChecked(Change.Id changeId) {
       InternalChangeQuery query = queryProvider.get().noFields();
       List<ChangeData> changes = query.byLegacyChangeId(changeId);
       if (changes.isEmpty()) {
@@ -131,7 +130,7 @@
           null, changeId, null, new Branch.NameKey(project, "INVALID_NOTE_DB_ONLY"), null);
     }
 
-    public ChangeNotes create(Project.NameKey project, Change.Id changeId) throws OrmException {
+    public ChangeNotes create(Project.NameKey project, Change.Id changeId) {
       checkArgument(project != null, "project is required");
       return new ChangeNotes(args, newChange(project, changeId), true, null).load();
     }
@@ -147,16 +146,15 @@
       return new ChangeNotes(args, change, true, null);
     }
 
-    public ChangeNotes createForBatchUpdate(Change change, boolean shouldExist)
-        throws OrmException {
+    public ChangeNotes createForBatchUpdate(Change change, boolean shouldExist) {
       return new ChangeNotes(args, change, shouldExist, null).load();
     }
 
-    public ChangeNotes create(Change change, RefCache refs) throws OrmException {
+    public ChangeNotes create(Change change, RefCache refs) {
       return new ChangeNotes(args, change, true, refs).load();
     }
 
-    public List<ChangeNotes> create(Collection<Change.Id> changeIds) throws OrmException {
+    public List<ChangeNotes> create(Collection<Change.Id> changeIds) {
       List<ChangeNotes> notes = new ArrayList<>();
       for (Change.Id changeId : changeIds) {
         try {
@@ -169,8 +167,9 @@
     }
 
     public List<ChangeNotes> create(
-        Project.NameKey project, Collection<Change.Id> changeIds, Predicate<ChangeNotes> predicate)
-        throws OrmException {
+        Project.NameKey project,
+        Collection<Change.Id> changeIds,
+        Predicate<ChangeNotes> predicate) {
       List<ChangeNotes> notes = new ArrayList<>();
       for (Change.Id cid : changeIds) {
         try {
@@ -229,7 +228,7 @@
       ChangeNotes n = new ChangeNotes(args, rawChangeFromNoteDb, true, null);
       try {
         n.load();
-      } catch (OrmException e) {
+      } catch (StorageException e) {
         return ChangeNotesResult.error(n.getChangeId(), e);
       }
       return ChangeNotesResult.notes(n);
@@ -238,7 +237,7 @@
     /** Result of {@link #scan(Repository,Project.NameKey)}. */
     @AutoValue
     public abstract static class ChangeNotesResult {
-      static ChangeNotesResult error(Change.Id id, OrmException e) {
+      static ChangeNotesResult error(Change.Id id, StorageException e) {
         return new AutoValue_ChangeNotes_Factory_ChangeNotesResult(id, Optional.of(e), null);
       }
 
@@ -251,7 +250,7 @@
       public abstract Change.Id id();
 
       /** Error encountered while loading this change, if any. */
-      public abstract Optional<OrmException> error();
+      public abstract Optional<StorageException> error();
 
       /**
        * Notes loaded for this change.
@@ -419,13 +418,12 @@
     return commentKeys;
   }
 
-  public ImmutableListMultimap<RevId, Comment> getDraftComments(Account.Id author)
-      throws OrmException {
+  public ImmutableListMultimap<RevId, Comment> getDraftComments(Account.Id author) {
     return getDraftComments(author, null);
   }
 
   public ImmutableListMultimap<RevId, Comment> getDraftComments(
-      Account.Id author, @Nullable Ref ref) throws OrmException {
+      Account.Id author, @Nullable Ref ref) {
     loadDraftComments(author, ref);
     // Filter out any zombie draft comments. These are drafts that are also in
     // the published map, and arise when the update to All-Users to delete them
@@ -435,7 +433,7 @@
             draftCommentNotes.getComments(), e -> !getCommentKeys().contains(e.getValue().key)));
   }
 
-  public ImmutableListMultimap<RevId, RobotComment> getRobotComments() throws OrmException {
+  public ImmutableListMultimap<RevId, RobotComment> getRobotComments() {
     loadRobotComments();
     return robotCommentNotes.getComments();
   }
@@ -445,14 +443,14 @@
    * However, this method will load the comments if no draft comments have been loaded or if the
    * caller would like the drafts for another author.
    */
-  private void loadDraftComments(Account.Id author, @Nullable Ref ref) throws OrmException {
+  private void loadDraftComments(Account.Id author, @Nullable Ref ref) {
     if (draftCommentNotes == null || !author.equals(draftCommentNotes.getAuthor()) || ref != null) {
       draftCommentNotes = new DraftCommentNotes(args, getChangeId(), author, ref);
       draftCommentNotes.load();
     }
   }
 
-  private void loadRobotComments() throws OrmException {
+  private void loadRobotComments() {
     if (robotCommentNotes == null) {
       robotCommentNotes = new RobotCommentNotes(args, change);
       robotCommentNotes.load();
@@ -468,7 +466,7 @@
     return robotCommentNotes;
   }
 
-  public boolean containsComment(Comment c) throws OrmException {
+  public boolean containsComment(Comment c) {
     if (containsCommentPublished(c)) {
       return true;
     }
diff --git a/java/com/google/gerrit/server/notedb/ChangeUpdate.java b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
index e1ef78a..ea65f21 100644
--- a/java/com/google/gerrit/server/notedb/ChangeUpdate.java
+++ b/java/com/google/gerrit/server/notedb/ChangeUpdate.java
@@ -49,8 +49,8 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Table;
 import com.google.common.collect.TreeBasedTable;
-import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
@@ -65,7 +65,6 @@
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.util.LabelVote;
 import com.google.gwtorm.client.IntKey;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
 import java.io.IOException;
@@ -102,18 +101,8 @@
  */
 public class ChangeUpdate extends AbstractChangeUpdate {
   public interface Factory {
-    ChangeUpdate create(ChangeNotes notes, CurrentUser user);
-
     ChangeUpdate create(ChangeNotes notes, CurrentUser user, Date when);
 
-    ChangeUpdate create(
-        Change change,
-        @Assisted("effective") @Nullable Account.Id accountId,
-        @Assisted("real") @Nullable Account.Id realAccountId,
-        PersonIdent authorIdent,
-        Date when,
-        Comparator<String> labelNameComparator);
-
     @VisibleForTesting
     ChangeUpdate create(
         ChangeNotes notes, CurrentUser user, Date when, Comparator<String> labelNameComparator);
@@ -167,30 +156,6 @@
       ProjectCache projectCache,
       @Assisted ChangeNotes notes,
       @Assisted CurrentUser user,
-      ChangeNoteUtil noteUtil) {
-    this(
-        serverIdent,
-        updateManagerFactory,
-        draftUpdateFactory,
-        robotCommentUpdateFactory,
-        deleteCommentRewriterFactory,
-        projectCache,
-        notes,
-        user,
-        serverIdent.getWhen(),
-        noteUtil);
-  }
-
-  @AssistedInject
-  private ChangeUpdate(
-      @GerritPersonIdent PersonIdent serverIdent,
-      NoteDbUpdateManager.Factory updateManagerFactory,
-      ChangeDraftUpdate.Factory draftUpdateFactory,
-      RobotCommentUpdate.Factory robotCommentUpdateFactory,
-      DeleteCommentRewriter.Factory deleteCommentRewriterFactory,
-      ProjectCache projectCache,
-      @Assisted ChangeNotes notes,
-      @Assisted CurrentUser user,
       @Assisted Date when,
       ChangeNoteUtil noteUtil) {
     this(
@@ -231,29 +196,7 @@
     this.approvals = approvals(labelNameComparator);
   }
 
-  @AssistedInject
-  private ChangeUpdate(
-      @GerritPersonIdent PersonIdent serverIdent,
-      NoteDbUpdateManager.Factory updateManagerFactory,
-      ChangeDraftUpdate.Factory draftUpdateFactory,
-      RobotCommentUpdate.Factory robotCommentUpdateFactory,
-      DeleteCommentRewriter.Factory deleteCommentRewriterFactory,
-      ChangeNoteUtil noteUtil,
-      @Assisted Change change,
-      @Assisted("effective") @Nullable Account.Id accountId,
-      @Assisted("real") @Nullable Account.Id realAccountId,
-      @Assisted PersonIdent authorIdent,
-      @Assisted Date when,
-      @Assisted Comparator<String> labelNameComparator) {
-    super(noteUtil, serverIdent, null, change, accountId, realAccountId, authorIdent, when);
-    this.draftUpdateFactory = draftUpdateFactory;
-    this.robotCommentUpdateFactory = robotCommentUpdateFactory;
-    this.updateManagerFactory = updateManagerFactory;
-    this.deleteCommentRewriterFactory = deleteCommentRewriterFactory;
-    this.approvals = approvals(labelNameComparator);
-  }
-
-  public ObjectId commit() throws IOException, OrmException {
+  public ObjectId commit() throws IOException {
     try (NoteDbUpdateManager updateManager = updateManagerFactory.create(getProjectName())) {
       updateManager.add(this);
       updateManager.execute();
@@ -486,7 +429,7 @@
 
   /** @return the tree id for the updated tree */
   private ObjectId storeRevisionNotes(RevWalk rw, ObjectInserter inserter, ObjectId curr)
-      throws ConfigInvalidException, OrmException, IOException {
+      throws ConfigInvalidException, IOException {
     if (comments.isEmpty() && pushCert == null) {
       return null;
     }
@@ -513,7 +456,7 @@
   }
 
   private RevisionNoteMap<ChangeRevisionNote> getRevisionNoteMap(RevWalk rw, ObjectId curr)
-      throws ConfigInvalidException, OrmException, IOException {
+      throws ConfigInvalidException, IOException {
     if (curr.equals(ObjectId.zeroId())) {
       return RevisionNoteMap.emptyMap();
     }
@@ -539,8 +482,7 @@
   }
 
   private void checkComments(
-      Map<RevId, ChangeRevisionNote> existingNotes, Map<RevId, RevisionNoteBuilder> toUpdate)
-      throws OrmException {
+      Map<RevId, ChangeRevisionNote> existingNotes, Map<RevId, RevisionNoteBuilder> toUpdate) {
     // Prohibit various kinds of illegal operations on comments.
     Set<Comment.Key> existing = new HashSet<>();
     for (ChangeRevisionNote rn : existingNotes.values()) {
@@ -570,7 +512,7 @@
     for (RevisionNoteBuilder b : toUpdate.values()) {
       for (Comment c : b.put.values()) {
         if (existing.contains(c.key)) {
-          throw new OrmException("Cannot update existing published comment: " + c);
+          throw new StorageException("Cannot update existing published comment: " + c);
         }
       }
     }
@@ -583,7 +525,7 @@
 
   @Override
   protected CommitBuilder applyImpl(RevWalk rw, ObjectInserter ins, ObjectId curr)
-      throws OrmException, IOException {
+      throws IOException {
     checkState(
         deleteCommentRewriter == null && deleteChangeMessageRewriter == null,
         "cannot update and rewrite ref in one BatchUpdate");
@@ -738,7 +680,7 @@
         cb.setTreeId(treeId);
       }
     } catch (ConfigInvalidException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
     return cb;
   }
diff --git a/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java b/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
index 4f1a5d0..c100550 100644
--- a/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
+++ b/java/com/google/gerrit/server/notedb/DeleteCommentRewriter.java
@@ -25,7 +25,6 @@
 import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.reviewdb.client.RevId;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
@@ -86,7 +85,7 @@
 
   @Override
   public ObjectId rewriteCommitHistory(RevWalk revWalk, ObjectInserter inserter, ObjectId currTip)
-      throws IOException, ConfigInvalidException, OrmException {
+      throws IOException, ConfigInvalidException {
     checkArgument(!currTip.equals(ObjectId.zeroId()));
 
     // Walk from the first commit of the branch.
diff --git a/java/com/google/gerrit/server/notedb/IntBlob.java b/java/com/google/gerrit/server/notedb/IntBlob.java
index f8c713c..6305a54 100644
--- a/java/com/google/gerrit/server/notedb/IntBlob.java
+++ b/java/com/google/gerrit/server/notedb/IntBlob.java
@@ -22,10 +22,10 @@
 import com.google.common.base.CharMatcher;
 import com.google.common.primitives.Ints;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.git.RefUpdateUtil;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 import java.util.Optional;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -41,20 +41,19 @@
 
 @AutoValue
 public abstract class IntBlob {
-  public static Optional<IntBlob> parse(Repository repo, String refName)
-      throws IOException, OrmException {
+  public static Optional<IntBlob> parse(Repository repo, String refName) throws IOException {
     try (ObjectReader or = repo.newObjectReader()) {
       return parse(repo, refName, or);
     }
   }
 
   public static Optional<IntBlob> parse(Repository repo, String refName, RevWalk rw)
-      throws IOException, OrmException {
+      throws IOException {
     return parse(repo, refName, rw.getObjectReader());
   }
 
   private static Optional<IntBlob> parse(Repository repo, String refName, ObjectReader or)
-      throws IOException, OrmException {
+      throws IOException {
     Ref ref = repo.exactRef(refName);
     if (ref == null) {
       return Optional.empty();
@@ -69,7 +68,7 @@
     String str = CharMatcher.whitespace().trimFrom(new String(ol.getCachedBytes(), UTF_8));
     Integer value = Ints.tryParse(str);
     if (value == null) {
-      throw new OrmException("invalid value in " + refName + " blob at " + id.name());
+      throw new StorageException("invalid value in " + refName + " blob at " + id.name());
     }
     return Optional.of(IntBlob.create(id, value));
   }
diff --git a/java/com/google/gerrit/server/notedb/NoteDbRewriter.java b/java/com/google/gerrit/server/notedb/NoteDbRewriter.java
index 3c7b0a3..19754d1 100644
--- a/java/com/google/gerrit/server/notedb/NoteDbRewriter.java
+++ b/java/com/google/gerrit/server/notedb/NoteDbRewriter.java
@@ -14,7 +14,6 @@
 
 package com.google.gerrit.server.notedb;
 
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 import org.eclipse.jgit.lib.ObjectId;
@@ -35,5 +34,5 @@
    * @return the {@code ObjectId} of the ref's new tip commit.
    */
   ObjectId rewriteCommitHistory(RevWalk revWalk, ObjectInserter inserter, ObjectId currTip)
-      throws IOException, ConfigInvalidException, OrmException;
+      throws IOException, ConfigInvalidException;
 }
diff --git a/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java b/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
index 7f23f87..ea42a9d 100644
--- a/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
+++ b/java/com/google/gerrit/server/notedb/NoteDbUpdateManager.java
@@ -25,6 +25,7 @@
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.MultimapBuilder;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.git.RefUpdateUtil;
 import com.google.gerrit.metrics.Timer1;
@@ -38,7 +39,6 @@
 import com.google.gerrit.server.git.InsertedObject;
 import com.google.gerrit.server.update.ChainedReceiveCommands;
 import com.google.gerrit.server.update.RetryingRestModifyView;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
@@ -348,10 +348,9 @@
   /**
    * Stage updates in the manager's internal list of commands.
    *
-   * @throws OrmException if a database layer error occurs.
    * @throws IOException if a storage layer error occurs.
    */
-  private void stage() throws OrmException, IOException {
+  private void stage() throws IOException {
     try (Timer1.Context timer = metrics.stageUpdateLatency.start(CHANGES)) {
       if (isEmpty()) {
         return;
@@ -376,12 +375,12 @@
   }
 
   @Nullable
-  public BatchRefUpdate execute() throws OrmException, IOException {
+  public BatchRefUpdate execute() throws IOException {
     return execute(false);
   }
 
   @Nullable
-  public BatchRefUpdate execute(boolean dryrun) throws OrmException, IOException {
+  public BatchRefUpdate execute(boolean dryrun) throws IOException {
     checkNotExecuted();
     if (isEmpty()) {
       executed = true;
@@ -476,7 +475,7 @@
     return -1;
   }
 
-  private void addCommands() throws OrmException, IOException {
+  private void addCommands() throws IOException {
     if (isEmpty()) {
       return;
     }
@@ -522,7 +521,7 @@
   }
 
   private static <U extends AbstractChangeUpdate> void addUpdates(
-      ListMultimap<String, U> all, OpenRepo or) throws OrmException, IOException {
+      ListMultimap<String, U> all, OpenRepo or) throws IOException {
     for (Map.Entry<String, Collection<U>> e : all.asMap().entrySet()) {
       String refName = e.getKey();
       Collection<U> updates = e.getValue();
@@ -537,7 +536,7 @@
       ObjectId curr = old;
       for (U u : updates) {
         if (u.isRootOnly() && !old.equals(ObjectId.zeroId())) {
-          throw new OrmException("Given ChangeUpdate is only allowed on initial commit");
+          throw new StorageException("Given ChangeUpdate is only allowed on initial commit");
         }
         ObjectId next = u.apply(or.rw, or.tempIns, curr);
         if (next == null) {
@@ -552,13 +551,13 @@
   }
 
   private static void addRewrites(ListMultimap<String, NoteDbRewriter> rewriters, OpenRepo openRepo)
-      throws OrmException, IOException {
+      throws IOException {
     for (Map.Entry<String, Collection<NoteDbRewriter>> entry : rewriters.asMap().entrySet()) {
       String refName = entry.getKey();
       ObjectId oldTip = openRepo.cmds.get(refName).orElse(ObjectId.zeroId());
 
       if (oldTip.equals(ObjectId.zeroId())) {
-        throw new OrmException(String.format("Ref %s is empty", refName));
+        throw new StorageException(String.format("Ref %s is empty", refName));
       }
 
       ObjectId currTip = oldTip;
@@ -571,7 +570,7 @@
           }
         }
       } catch (ConfigInvalidException e) {
-        throw new OrmException("Cannot rewrite commit history", e);
+        throw new StorageException("Cannot rewrite commit history", e);
       }
 
       if (!oldTip.equals(currTip)) {
diff --git a/java/com/google/gerrit/server/notedb/RepoSequence.java b/java/com/google/gerrit/server/notedb/RepoSequence.java
index 56264e9..3919622 100644
--- a/java/com/google/gerrit/server/notedb/RepoSequence.java
+++ b/java/com/google/gerrit/server/notedb/RepoSequence.java
@@ -30,12 +30,12 @@
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.util.concurrent.Runnables;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.git.RefUpdateUtil;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -65,7 +65,7 @@
 public class RepoSequence {
   @FunctionalInterface
   public interface Seed {
-    int get() throws OrmException;
+    int get();
   }
 
   @VisibleForTesting
@@ -184,7 +184,7 @@
     counterLock = new ReentrantLock(true);
   }
 
-  public int next() throws OrmException {
+  public int next() {
     counterLock.lock();
     try {
       if (counter >= limit) {
@@ -196,7 +196,7 @@
     }
   }
 
-  public ImmutableList<Integer> next(int count) throws OrmException {
+  public ImmutableList<Integer> next(int count) {
     if (count == 0) {
       return ImmutableList.of();
     }
@@ -221,7 +221,7 @@
   }
 
   @VisibleForTesting
-  public void set(int val) throws OrmException {
+  public void set(int val) {
     // Don't bother spinning. This is only for tests, and a test that calls set
     // concurrently with other writes is doing it wrong.
     counterLock.lock();
@@ -231,14 +231,14 @@
         IntBlob.store(repo, rw, projectName, refName, null, val, gitRefUpdated);
         counter = limit;
       } catch (IOException e) {
-        throw new OrmException(e);
+        throw new StorageException(e);
       }
     } finally {
       counterLock.unlock();
     }
   }
 
-  public void increaseTo(int val) throws OrmException {
+  public void increaseTo(int val) {
     counterLock.lock();
     try {
       try (Repository repo = repoManager.openRepository(projectName);
@@ -252,18 +252,18 @@
         counter = limit;
       } catch (ExecutionException | RetryException e) {
         if (e.getCause() != null) {
-          Throwables.throwIfInstanceOf(e.getCause(), OrmException.class);
+          Throwables.throwIfInstanceOf(e.getCause(), StorageException.class);
         }
-        throw new OrmException(e);
+        throw new StorageException(e);
       } catch (IOException e) {
-        throw new OrmException(e);
+        throw new StorageException(e);
       }
     } finally {
       counterLock.unlock();
     }
   }
 
-  private void acquire(int count) throws OrmException {
+  private void acquire(int count) {
     try (Repository repo = repoManager.openRepository(projectName);
         RevWalk rw = new RevWalk(repo)) {
       TryAcquire attempt = new TryAcquire(repo, rw, count);
@@ -273,11 +273,11 @@
       acquireCount++;
     } catch (ExecutionException | RetryException e) {
       if (e.getCause() != null) {
-        Throwables.throwIfInstanceOf(e.getCause(), OrmException.class);
+        Throwables.throwIfInstanceOf(e.getCause(), StorageException.class);
       }
-      throw new OrmException(e);
+      throw new StorageException(e);
     } catch (IOException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java b/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java
index 9307724..0304ab8 100644
--- a/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java
+++ b/java/com/google/gerrit/server/notedb/RobotCommentUpdate.java
@@ -19,13 +19,13 @@
 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
 
 import com.google.common.collect.Sets;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RevId;
 import com.google.gerrit.reviewdb.client.RobotComment;
 import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
 import java.io.IOException;
@@ -101,7 +101,7 @@
 
   private CommitBuilder storeCommentsInNotes(
       RevWalk rw, ObjectInserter ins, ObjectId curr, CommitBuilder cb)
-      throws ConfigInvalidException, OrmException, IOException {
+      throws ConfigInvalidException, IOException {
     RevisionNoteMap<RobotCommentsRevisionNote> rnm = getRevisionNoteMap(rw, curr);
     Set<RevId> updatedRevs = Sets.newHashSetWithExpectedSize(rnm.revisionNotes.size());
     RevisionNoteBuilder.Cache cache = new RevisionNoteBuilder.Cache(rnm);
@@ -148,7 +148,7 @@
   }
 
   private RevisionNoteMap<RobotCommentsRevisionNote> getRevisionNoteMap(RevWalk rw, ObjectId curr)
-      throws ConfigInvalidException, OrmException, IOException {
+      throws ConfigInvalidException, IOException {
     if (curr.equals(ObjectId.zeroId())) {
       return RevisionNoteMap.emptyMap();
     }
@@ -179,13 +179,13 @@
 
   @Override
   protected CommitBuilder applyImpl(RevWalk rw, ObjectInserter ins, ObjectId curr)
-      throws OrmException, IOException {
+      throws IOException {
     CommitBuilder cb = new CommitBuilder();
     cb.setMessage("Update robot comments");
     try {
       return storeCommentsInNotes(rw, ins, curr, cb);
     } catch (ConfigInvalidException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/notedb/Sequences.java b/java/com/google/gerrit/server/notedb/Sequences.java
index 1a255f14..9e8c541 100644
--- a/java/com/google/gerrit/server/notedb/Sequences.java
+++ b/java/com/google/gerrit/server/notedb/Sequences.java
@@ -25,7 +25,6 @@
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import org.eclipse.jgit.lib.Config;
@@ -100,25 +99,25 @@
             Field.ofBoolean("multiple"));
   }
 
-  public int nextAccountId() throws OrmException {
+  public int nextAccountId() {
     try (Timer2.Context timer = nextIdLatency.start(SequenceType.ACCOUNTS, false)) {
       return accountSeq.next();
     }
   }
 
-  public int nextChangeId() throws OrmException {
+  public int nextChangeId() {
     try (Timer2.Context timer = nextIdLatency.start(SequenceType.CHANGES, false)) {
       return changeSeq.next();
     }
   }
 
-  public ImmutableList<Integer> nextChangeIds(int count) throws OrmException {
+  public ImmutableList<Integer> nextChangeIds(int count) {
     try (Timer2.Context timer = nextIdLatency.start(SequenceType.CHANGES, count > 1)) {
       return changeSeq.next(count);
     }
   }
 
-  public int nextGroupId() throws OrmException {
+  public int nextGroupId() {
     try (Timer2.Context timer = nextIdLatency.start(SequenceType.GROUPS, false)) {
       return groupSeq.next();
     }
diff --git a/java/com/google/gerrit/server/patch/PatchFile.java b/java/com/google/gerrit/server/patch/PatchFile.java
index aff519a..1593b11 100644
--- a/java/com/google/gerrit/server/patch/PatchFile.java
+++ b/java/com/google/gerrit/server/patch/PatchFile.java
@@ -16,7 +16,7 @@
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
-import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.exceptions.NoSuchEntityException;
 import com.google.gerrit.reviewdb.client.Patch;
 import java.io.IOException;
 import org.eclipse.jgit.errors.CorruptObjectException;
diff --git a/java/com/google/gerrit/server/patch/PatchScriptFactory.java b/java/com/google/gerrit/server/patch/PatchScriptFactory.java
index 50d3711..08ee7a3 100644
--- a/java/com/google/gerrit/server/patch/PatchScriptFactory.java
+++ b/java/com/google/gerrit/server/patch/PatchScriptFactory.java
@@ -43,7 +43,6 @@
 import com.google.gerrit.server.project.InvalidChangeOperationException;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Provider;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
@@ -187,8 +186,8 @@
 
   @Override
   public PatchScript call()
-      throws OrmException, LargeObjectException, AuthException, InvalidChangeOperationException,
-          IOException, PermissionBackendException {
+      throws LargeObjectException, AuthException, InvalidChangeOperationException, IOException,
+          PermissionBackendException {
     if (parentNum < 0) {
       validatePatchSetId(psa);
     }
@@ -259,7 +258,7 @@
     return b;
   }
 
-  private ObjectId toObjectId(PatchSet ps) throws AuthException, IOException, OrmException {
+  private ObjectId toObjectId(PatchSet ps) throws AuthException, IOException {
     if (ps.getId().get() == 0) {
       return getEditRev();
     }
@@ -275,7 +274,7 @@
     }
   }
 
-  private ObjectId getEditRev() throws AuthException, IOException, OrmException {
+  private ObjectId getEditRev() throws AuthException, IOException {
     edit = editReader.byChange(notes);
     if (edit.isPresent()) {
       return edit.get().getEditCommit();
@@ -291,8 +290,7 @@
     }
   }
 
-  private void loadCommentsAndHistory(ChangeType changeType, String oldName, String newName)
-      throws OrmException {
+  private void loadCommentsAndHistory(ChangeType changeType, String oldName, String newName) {
     Map<Patch.Key, Patch> byKey = new HashMap<>();
 
     if (loadHistory) {
@@ -384,7 +382,7 @@
     }
   }
 
-  private void loadPublished(Map<Patch.Key, Patch> byKey, String file) throws OrmException {
+  private void loadPublished(Map<Patch.Key, Patch> byKey, String file) {
     for (Comment c : commentsUtil.publishedByChangeFile(notes, file)) {
       comments.include(notes.getChangeId(), c);
       PatchSet.Id psId = new PatchSet.Id(notes.getChangeId(), c.key.patchSetId);
@@ -396,8 +394,7 @@
     }
   }
 
-  private void loadDrafts(Map<Patch.Key, Patch> byKey, Account.Id me, String file)
-      throws OrmException {
+  private void loadDrafts(Map<Patch.Key, Patch> byKey, Account.Id me, String file) {
     for (Comment c : commentsUtil.draftByChangeFileAuthor(notes, file, me)) {
       comments.include(notes.getChangeId(), c);
       PatchSet.Id psId = new PatchSet.Id(notes.getChangeId(), c.key.patchSetId);
diff --git a/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java b/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
index f11f116..0eb5588 100644
--- a/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
+++ b/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.patch;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetInfo;
@@ -24,7 +25,6 @@
 import com.google.gerrit.server.account.Emails;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -53,8 +53,7 @@
     this.emails = emails;
   }
 
-  public PatchSetInfo get(RevWalk rw, RevCommit src, PatchSet.Id psi)
-      throws IOException, OrmException {
+  public PatchSetInfo get(RevWalk rw, RevCommit src, PatchSet.Id psi) throws IOException {
     rw.parseBody(src);
     PatchSetInfo info = new PatchSetInfo(psi);
     info.setSubject(src.getShortMessage());
@@ -70,7 +69,7 @@
     try {
       PatchSet patchSet = psUtil.get(notes, psId);
       return get(notes.getProjectName(), patchSet);
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       throw new PatchSetInfoNotAvailableException(e);
     }
   }
@@ -83,13 +82,13 @@
       PatchSetInfo info = get(rw, src, patchSet.getId());
       info.setParents(toParentInfos(src.getParents(), rw));
       return info;
-    } catch (IOException | OrmException e) {
+    } catch (IOException | StorageException e) {
       throw new PatchSetInfoNotAvailableException(e);
     }
   }
 
   // TODO: The same method exists in EventFactory, find a common place for it
-  private UserIdentity toUserIdentity(PersonIdent who) throws IOException, OrmException {
+  private UserIdentity toUserIdentity(PersonIdent who) throws IOException {
     final UserIdentity u = new UserIdentity();
     u.setName(who.getName());
     u.setEmail(who.getEmailAddress());
diff --git a/java/com/google/gerrit/server/permissions/ChangeControl.java b/java/com/google/gerrit/server/permissions/ChangeControl.java
index 309bf51..ee362002 100644
--- a/java/com/google/gerrit/server/permissions/ChangeControl.java
+++ b/java/com/google/gerrit/server/permissions/ChangeControl.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRange;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.conditions.BooleanCondition;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.Account;
@@ -31,7 +32,6 @@
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.permissions.PermissionBackend.ForChange;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.Collection;
@@ -52,8 +52,7 @@
       this.notesFactory = notesFactory;
     }
 
-    ChangeControl create(RefControl refControl, Project.NameKey project, Change.Id changeId)
-        throws OrmException {
+    ChangeControl create(RefControl refControl, Project.NameKey project, Change.Id changeId) {
       return create(refControl, notesFactory.create(project, changeId));
     }
 
@@ -90,7 +89,7 @@
   }
 
   /** Can this user see this change? */
-  private boolean isVisible(@Nullable ChangeData cd) throws OrmException {
+  private boolean isVisible(@Nullable ChangeData cd) {
     if (getChange().isPrivate() && !isPrivateVisible(cd)) {
       return false;
     }
@@ -154,7 +153,7 @@
   }
 
   /** Is this user a reviewer for the change? */
-  private boolean isReviewer(@Nullable ChangeData cd) throws OrmException {
+  private boolean isReviewer(@Nullable ChangeData cd) {
     if (getUser().isIdentifiedUser()) {
       cd = cd != null ? cd : changeDataFactory.create(notes);
       Collection<Account.Id> results = cd.reviewers().all();
@@ -212,7 +211,7 @@
         || getProjectControl().isAdmin();
   }
 
-  private boolean isPrivateVisible(ChangeData cd) throws OrmException {
+  private boolean isPrivateVisible(ChangeData cd) {
     return isOwner()
         || isReviewer(cd)
         || refControl.canPerform(Permission.VIEW_PRIVATE_CHANGES)
@@ -314,7 +313,7 @@
           case SUBMIT_AS:
             return refControl.canPerform(changePermissionName(perm));
         }
-      } catch (OrmException e) {
+      } catch (StorageException e) {
         throw new PermissionBackendException("unavailable", e);
       }
       throw new PermissionBackendException(perm + " unsupported");
diff --git a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
index 913921f..a5174e1 100644
--- a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
+++ b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
@@ -31,6 +31,7 @@
 import com.google.common.collect.Maps;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.metrics.Counter0;
 import com.google.gerrit.metrics.Description;
@@ -54,7 +55,6 @@
 import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
@@ -393,7 +393,7 @@
         }
       }
       return visibleChanges;
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log(
           "Cannot load changes for project %s, assuming no changes are visible", project);
       return Collections.emptyMap();
@@ -503,7 +503,7 @@
     ChangeNotes notes;
     try {
       notes = changeNotesFactory.create(projectState.getNameKey(), cId);
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       throw new PermissionBackendException("can't construct change notes", e);
     }
     try {
diff --git a/java/com/google/gerrit/server/permissions/PermissionBackend.java b/java/com/google/gerrit/server/permissions/PermissionBackend.java
index 3ac294a..3d10181 100644
--- a/java/com/google/gerrit/server/permissions/PermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/PermissionBackend.java
@@ -23,6 +23,7 @@
 import com.google.common.collect.Sets;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.access.CoreOrPluginProjectPermission;
 import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
 import com.google.gerrit.extensions.conditions.BooleanCondition;
@@ -34,7 +35,6 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.ImplementedBy;
 import java.util.Collection;
 import java.util.Collections;
@@ -165,7 +165,7 @@
     public ForChange change(ChangeData cd) {
       try {
         return ref(cd.change().getDest()).change(cd);
-      } catch (OrmException e) {
+      } catch (StorageException e) {
         return FailedPermissionBackend.change("unavailable", e);
       }
     }
@@ -281,7 +281,7 @@
     public ForChange change(ChangeData cd) {
       try {
         return ref(cd.change().getDest().get()).change(cd);
-      } catch (OrmException e) {
+      } catch (StorageException e) {
         return FailedPermissionBackend.change("unavailable", e);
       }
     }
diff --git a/java/com/google/gerrit/server/permissions/ProjectControl.java b/java/com/google/gerrit/server/permissions/ProjectControl.java
index 144519a..e370151 100644
--- a/java/com/google/gerrit/server/permissions/ProjectControl.java
+++ b/java/com/google/gerrit/server/permissions/ProjectControl.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.access.CoreOrPluginProjectPermission;
 import com.google.gerrit.extensions.api.access.PluginProjectPermission;
 import com.google.gerrit.extensions.conditions.BooleanCondition;
@@ -43,7 +44,6 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.project.SectionMatcher;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Collection;
@@ -99,7 +99,7 @@
     return new ForProjectImpl();
   }
 
-  ChangeControl controlFor(Change change) throws OrmException {
+  ChangeControl controlFor(Change change) {
     return changeControlFactory.create(
         controlForRef(change.getDest()), change.getProject(), change.getId());
   }
@@ -353,7 +353,7 @@
       try {
         checkProject(cd.change());
         return super.change(cd);
-      } catch (OrmException e) {
+      } catch (StorageException e) {
         return FailedPermissionBackend.change("unavailable", e);
       }
     }
diff --git a/java/com/google/gerrit/server/permissions/RefControl.java b/java/com/google/gerrit/server/permissions/RefControl.java
index 60fd15b..9a2ecdd 100644
--- a/java/com/google/gerrit/server/permissions/RefControl.java
+++ b/java/com/google/gerrit/server/permissions/RefControl.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.common.data.PermissionRange;
 import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.common.data.PermissionRule.Action;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.conditions.BooleanCondition;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.reviewdb.client.Change;
@@ -33,7 +34,6 @@
 import com.google.gerrit.server.permissions.PermissionBackend.ForRef;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.util.MagicBranch;
-import com.google.gwtorm.server.OrmException;
 import java.util.Collection;
 import java.util.EnumSet;
 import java.util.List;
@@ -442,7 +442,7 @@
       try {
         // TODO(hiesel) Force callers to call database() and use db instead of cd.db()
         return getProjectControl().controlFor(cd.change()).asForChange(cd);
-      } catch (OrmException e) {
+      } catch (StorageException e) {
         return FailedPermissionBackend.change("unavailable", e);
       }
     }
diff --git a/java/com/google/gerrit/server/project/NoSuchChangeException.java b/java/com/google/gerrit/server/project/NoSuchChangeException.java
index 7946a3a..6f65659 100644
--- a/java/com/google/gerrit/server/project/NoSuchChangeException.java
+++ b/java/com/google/gerrit/server/project/NoSuchChangeException.java
@@ -14,11 +14,11 @@
 
 package com.google.gerrit.server.project;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwtorm.server.OrmException;
 
 /** Indicates the change does not exist. */
-public class NoSuchChangeException extends OrmException {
+public class NoSuchChangeException extends StorageException {
   private static final long serialVersionUID = 1L;
 
   public NoSuchChangeException(Change.Id key) {
diff --git a/java/com/google/gerrit/server/project/ProjectConfig.java b/java/com/google/gerrit/server/project/ProjectConfig.java
index 6dd697f..3700556 100644
--- a/java/com/google/gerrit/server/project/ProjectConfig.java
+++ b/java/com/google/gerrit/server/project/ProjectConfig.java
@@ -41,9 +41,8 @@
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
 import com.google.gerrit.common.data.PermissionRule.Action;
-import com.google.gerrit.common.data.RefConfigSection;
 import com.google.gerrit.common.data.SubscribeSection;
-import com.google.gerrit.common.errors.InvalidNameException;
+import com.google.gerrit.exceptions.InvalidNameException;
 import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.extensions.client.ProjectState;
 import com.google.gerrit.mail.Address;
@@ -742,7 +741,7 @@
     accessSections = new HashMap<>();
     sectionsWithUnknownPermissions = new HashSet<>();
     for (String refName : rc.getSubsections(ACCESS)) {
-      if (RefConfigSection.isValid(refName) && isValidRegex(refName)) {
+      if (AccessSection.isValidRefSectionName(refName) && isValidRegex(refName)) {
         AccessSection as = getAccessSection(refName, true);
 
         for (String varName : rc.getStringList(ACCESS, refName, KEY_GROUP_PERMISSIONS)) {
@@ -1373,7 +1372,7 @@
     }
 
     for (String name : rc.getSubsections(ACCESS)) {
-      if (RefConfigSection.isValid(name) && !accessSections.containsKey(name)) {
+      if (AccessSection.isValidRefSectionName(name) && !accessSections.containsKey(name)) {
         rc.unsetSection(ACCESS, name);
       }
     }
diff --git a/java/com/google/gerrit/server/project/ProjectState.java b/java/com/google/gerrit/server/project/ProjectState.java
index 8d6923b..921d382 100644
--- a/java/com/google/gerrit/server/project/ProjectState.java
+++ b/java/com/google/gerrit/server/project/ProjectState.java
@@ -27,7 +27,6 @@
 import com.google.gerrit.common.data.LabelTypes;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
-import com.google.gerrit.common.data.RefConfigSection;
 import com.google.gerrit.common.data.SubscribeSection;
 import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
 import com.google.gerrit.extensions.client.SubmitType;
@@ -482,7 +481,7 @@
             continue;
           }
 
-          if (RefConfigSection.isValid(refPattern) && match(destination, refPattern)) {
+          if (AccessSection.isValidRefSectionName(refPattern) && match(destination, refPattern)) {
             r.add(l);
             break;
           }
diff --git a/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java b/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
index 894037d..5b0a5e6 100644
--- a/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
+++ b/java/com/google/gerrit/server/project/ProjectsConsistencyChecker.java
@@ -24,6 +24,7 @@
 import com.google.common.base.Strings;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.FixInput;
 import com.google.gerrit.extensions.api.projects.CheckProjectInput;
 import com.google.gerrit.extensions.api.projects.CheckProjectInput.AutoCloseableChangesCheckInput;
@@ -51,7 +52,6 @@
 import com.google.gerrit.server.query.change.RefPredicate;
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryHelper.ActionType;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -94,7 +94,7 @@
   }
 
   public CheckProjectResultInfo check(Project.NameKey projectName, CheckProjectInput input)
-      throws IOException, OrmException, RestApiException {
+      throws IOException, RestApiException {
     CheckProjectResultInfo r = new CheckProjectResultInfo();
     if (input.autoCloseableChangesCheck != null) {
       r.autoCloseableChangesCheckResult =
@@ -105,7 +105,7 @@
 
   private AutoCloseableChangesCheckResult checkForAutoCloseableChanges(
       Project.NameKey projectName, AutoCloseableChangesCheckInput input)
-      throws IOException, OrmException, RestApiException {
+      throws IOException, RestApiException {
     AutoCloseableChangesCheckResult r = new AutoCloseableChangesCheckResult();
     if (Strings.isNullOrEmpty(input.branch)) {
       throw new BadRequestException("branch is required");
@@ -256,8 +256,7 @@
       List<Predicate<ChangeData>> predicates,
       boolean fix,
       Map<Change.Key, ObjectId> changeIdToMergedSha1,
-      List<ObjectId> mergedSha1s)
-      throws OrmException {
+      List<ObjectId> mergedSha1s) {
     if (predicates.isEmpty()) {
       return ImmutableList.of();
     }
@@ -273,7 +272,7 @@
                     .setRequestedFields(ChangeField.CHANGE, ChangeField.PATCH_SET)
                     .query(and(basePredicate, or(predicates)));
               },
-              OrmException.class::isInstance);
+              StorageException.class::isInstance);
 
       // Result for this query that we want to return to the client.
       List<ChangeInfo> autoCloseableChangesByBranch = new ArrayList<>();
@@ -307,15 +306,15 @@
                 }
                 return null;
               },
-              OrmException.class::isInstance);
+              StorageException.class::isInstance);
         }
       }
 
       return autoCloseableChangesByBranch;
     } catch (Exception e) {
       Throwables.throwIfUnchecked(e);
-      Throwables.throwIfInstanceOf(e, OrmException.class);
-      throw new OrmException(e);
+      Throwables.throwIfInstanceOf(e, StorageException.class);
+      throw new StorageException(e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/project/RefPattern.java b/java/com/google/gerrit/server/project/RefPattern.java
index 72face2..0e916fb 100644
--- a/java/com/google/gerrit/server/project/RefPattern.java
+++ b/java/com/google/gerrit/server/project/RefPattern.java
@@ -19,8 +19,7 @@
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.data.RefConfigSection;
-import com.google.gerrit.common.errors.InvalidNameException;
+import com.google.gerrit.exceptions.InvalidNameException;
 import dk.brics.automaton.RegExp;
 import java.util.concurrent.ExecutionException;
 import java.util.regex.Pattern;
@@ -78,11 +77,11 @@
   }
 
   public static void validate(String refPattern) throws InvalidNameException {
-    if (refPattern.startsWith(RefConfigSection.REGEX_PREFIX)) {
+    if (refPattern.startsWith(AccessSection.REGEX_PREFIX)) {
       if (!Repository.isValidRefName(shortestExample(refPattern))) {
         throw new InvalidNameException(refPattern);
       }
-    } else if (refPattern.equals(RefConfigSection.ALL)) {
+    } else if (refPattern.equals(AccessSection.ALL)) {
       // This is a special case we have to allow, it fails below.
     } else if (refPattern.endsWith("/*")) {
       String prefix = refPattern.substring(0, refPattern.length() - 2);
diff --git a/java/com/google/gerrit/server/project/RemoveReviewerControl.java b/java/com/google/gerrit/server/project/RemoveReviewerControl.java
index 7353ce8..eeb2a65 100644
--- a/java/com/google/gerrit/server/project/RemoveReviewerControl.java
+++ b/java/com/google/gerrit/server/project/RemoveReviewerControl.java
@@ -26,7 +26,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.RefPermission;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -66,7 +65,7 @@
   /** @return true if the user is allowed to remove this reviewer. */
   public boolean testRemoveReviewer(
       ChangeData cd, CurrentUser currentUser, Account.Id reviewer, int value)
-      throws PermissionBackendException, OrmException {
+      throws PermissionBackendException {
     if (canRemoveReviewerWithoutPermissionCheck(
         permissionBackend, cd.change(), currentUser, reviewer, value)) {
       return true;
diff --git a/java/com/google/gerrit/server/project/SectionMatcher.java b/java/com/google/gerrit/server/project/SectionMatcher.java
index 3095f2f..a8ebd98 100644
--- a/java/com/google/gerrit/server/project/SectionMatcher.java
+++ b/java/com/google/gerrit/server/project/SectionMatcher.java
@@ -27,7 +27,7 @@
 public class SectionMatcher extends RefPatternMatcher {
   static SectionMatcher wrap(Project.NameKey project, AccessSection section) {
     String ref = section.getName();
-    if (AccessSection.isValid(ref)) {
+    if (AccessSection.isValidRefSectionName(ref)) {
       return new SectionMatcher(project, section, getMatcher(ref));
     }
     return null;
diff --git a/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java b/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
index 1afa426..85d91e7 100644
--- a/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
+++ b/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
@@ -18,12 +18,12 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.rules.PrologRule;
 import com.google.gerrit.server.rules.SubmitRule;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Collection;
@@ -91,14 +91,14 @@
     try {
       change = cd.change();
       if (change == null) {
-        throw new OrmException("Change not found");
+        throw new StorageException("Change not found");
       }
 
       projectState = projectCache.get(cd.project());
       if (projectState == null) {
         throw new NoSuchProjectException(cd.project());
       }
-    } catch (OrmException | NoSuchProjectException e) {
+    } catch (StorageException | NoSuchProjectException e) {
       return ruleError("Error looking up change " + cd.getId(), e);
     }
 
diff --git a/java/com/google/gerrit/server/query/account/AccountIsVisibleToPredicate.java b/java/com/google/gerrit/server/query/account/AccountIsVisibleToPredicate.java
index bd7b7fe..f4ff441 100644
--- a/java/com/google/gerrit/server/query/account/AccountIsVisibleToPredicate.java
+++ b/java/com/google/gerrit/server/query/account/AccountIsVisibleToPredicate.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.server.account.AccountControl;
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.index.IndexUtils;
-import com.google.gwtorm.server.OrmException;
 
 public class AccountIsVisibleToPredicate extends IsVisibleToPredicate<AccountState> {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -32,7 +31,7 @@
   }
 
   @Override
-  public boolean match(AccountState accountState) throws OrmException {
+  public boolean match(AccountState accountState) {
     boolean canSee = accountControl.canSee(accountState);
     if (!canSee) {
       logger.atFine().log("Filter out non-visisble account: %s", accountState);
diff --git a/java/com/google/gerrit/server/query/account/AccountPredicates.java b/java/com/google/gerrit/server/query/account/AccountPredicates.java
index 732177d..cb96bc5 100644
--- a/java/com/google/gerrit/server/query/account/AccountPredicates.java
+++ b/java/com/google/gerrit/server/query/account/AccountPredicates.java
@@ -27,7 +27,6 @@
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.index.account.AccountField;
 import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
 import java.util.List;
 
 public class AccountPredicates {
@@ -140,7 +139,7 @@
     }
 
     @Override
-    public boolean match(AccountState object) throws OrmException {
+    public boolean match(AccountState object) {
       return true;
     }
 
diff --git a/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java b/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
index b7e8a46..70f4a2d 100644
--- a/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
@@ -17,7 +17,8 @@
 import com.google.common.collect.Lists;
 import com.google.common.flogger.FluentLogger;
 import com.google.common.primitives.Ints;
-import com.google.gerrit.common.errors.NotSignedInException;
+import com.google.gerrit.exceptions.NotSignedInException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.index.Index;
 import com.google.gerrit.index.Schema;
@@ -37,13 +38,12 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.ProvisionException;
 
 /** Parses a query string meant to be applied to account objects. */
-public class AccountQueryBuilder extends QueryBuilder<AccountState> {
+public class AccountQueryBuilder extends QueryBuilder<AccountState, AccountQueryBuilder> {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   public static final String FIELD_ACCOUNT = "account";
@@ -108,13 +108,13 @@
 
   @Inject
   AccountQueryBuilder(Arguments args) {
-    super(mydef);
+    super(mydef, null);
     this.args = args;
   }
 
   @Operator
   public Predicate<AccountState> cansee(String change)
-      throws QueryParseException, OrmException, PermissionBackendException {
+      throws QueryParseException, PermissionBackendException {
     ChangeNotes changeNotes = args.changeFinder.findOne(change);
     if (changeNotes == null) {
       throw error(String.format("change %s not found", change));
@@ -195,7 +195,7 @@
     if (query.startsWith("cansee:")) {
       try {
         return cansee(query.substring(7));
-      } catch (OrmException | QueryParseException | PermissionBackendException e) {
+      } catch (StorageException | QueryParseException | PermissionBackendException e) {
         // Ignore, fall back to default query
       }
     }
diff --git a/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java b/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
index 1cb2170..8bbeb24 100644
--- a/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
+++ b/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.query.account;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.index.query.PostFilterPredicate;
 import com.google.gerrit.server.account.AccountState;
@@ -21,7 +22,6 @@
 import com.google.gerrit.server.permissions.ChangePermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 
 public class CanSeeChangePredicate extends PostFilterPredicate<AccountState> {
   private final PermissionBackend permissionBackend;
@@ -34,7 +34,7 @@
   }
 
   @Override
-  public boolean match(AccountState accountState) throws OrmException {
+  public boolean match(AccountState accountState) {
     try {
       permissionBackend
           .absentUser(accountState.getAccount().getId())
@@ -42,7 +42,7 @@
           .check(ChangePermission.READ);
       return true;
     } catch (PermissionBackendException e) {
-      throw new OrmException("Failed to check if account can see change", e);
+      throw new StorageException("Failed to check if account can see change", e);
     } catch (AuthException e) {
       return false;
     }
diff --git a/java/com/google/gerrit/server/query/account/InternalAccountQuery.java b/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
index 61a9690..490991a 100644
--- a/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
+++ b/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
@@ -30,7 +30,6 @@
 import com.google.gerrit.server.account.externalids.ExternalId;
 import com.google.gerrit.server.index.account.AccountField;
 import com.google.gerrit.server.index.account.AccountIndexCollection;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.util.Arrays;
 import java.util.List;
@@ -51,19 +50,19 @@
     super(queryProcessor, indexes, indexConfig);
   }
 
-  public List<AccountState> byDefault(String query) throws OrmException {
+  public List<AccountState> byDefault(String query) {
     return query(AccountPredicates.defaultPredicate(schema(), true, query));
   }
 
-  public List<AccountState> byExternalId(String scheme, String id) throws OrmException {
+  public List<AccountState> byExternalId(String scheme, String id) {
     return byExternalId(ExternalId.Key.create(scheme, id));
   }
 
-  public List<AccountState> byExternalId(ExternalId.Key externalId) throws OrmException {
+  public List<AccountState> byExternalId(ExternalId.Key externalId) {
     return query(AccountPredicates.externalIdIncludingSecondaryEmails(externalId.toString()));
   }
 
-  public List<AccountState> byFullName(String fullName) throws OrmException {
+  public List<AccountState> byFullName(String fullName) {
     return query(AccountPredicates.fullName(fullName));
   }
 
@@ -72,9 +71,8 @@
    *
    * @param email preferred email by which accounts should be found
    * @return list of accounts that have a preferred email that exactly matches the given email
-   * @throws OrmException if query cannot be parsed
    */
-  public List<AccountState> byPreferredEmail(String email) throws OrmException {
+  public List<AccountState> byPreferredEmail(String email) {
     if (hasPreferredEmailExact()) {
       return query(AccountPredicates.preferredEmailExact(email));
     }
@@ -94,9 +92,8 @@
    * @param emails preferred emails by which accounts should be found
    * @return multimap of the given emails to accounts that have a preferred email that exactly
    *     matches this email
-   * @throws OrmException if query cannot be parsed
    */
-  public Multimap<String, AccountState> byPreferredEmail(String... emails) throws OrmException {
+  public Multimap<String, AccountState> byPreferredEmail(String... emails) {
     List<String> emailList = Arrays.asList(emails);
 
     if (hasPreferredEmailExact()) {
@@ -127,7 +124,7 @@
     return accountsByEmail;
   }
 
-  public List<AccountState> byWatchedProject(Project.NameKey project) throws OrmException {
+  public List<AccountState> byWatchedProject(Project.NameKey project) {
     return query(AccountPredicates.watchedProject(project));
   }
 
diff --git a/java/com/google/gerrit/server/query/change/AddedPredicate.java b/java/com/google/gerrit/server/query/change/AddedPredicate.java
index 099e841..1f526c5 100644
--- a/java/com/google/gerrit/server/query/change/AddedPredicate.java
+++ b/java/com/google/gerrit/server/query/change/AddedPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class AddedPredicate extends IntegerRangeChangePredicate {
   public AddedPredicate(String value) throws QueryParseException {
@@ -24,7 +23,7 @@
   }
 
   @Override
-  protected Integer getValueInt(ChangeData changeData) throws OrmException {
+  protected Integer getValueInt(ChangeData changeData) {
     return ChangeField.ADDED.get(changeData);
   }
 }
diff --git a/java/com/google/gerrit/server/query/change/AfterPredicate.java b/java/com/google/gerrit/server/query/change/AfterPredicate.java
index de57b3b..df5a71d 100644
--- a/java/com/google/gerrit/server/query/change/AfterPredicate.java
+++ b/java/com/google/gerrit/server/query/change/AfterPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 import java.util.Date;
 
 public class AfterPredicate extends TimestampRangeChangePredicate {
@@ -38,7 +37,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     return cd.change().getLastUpdatedOn().getTime() >= cut.getTime();
   }
 }
diff --git a/java/com/google/gerrit/server/query/change/AgePredicate.java b/java/com/google/gerrit/server/query/change/AgePredicate.java
index 29f1b8a..1cf2c2f 100644
--- a/java/com/google/gerrit/server/query/change/AgePredicate.java
+++ b/java/com/google/gerrit/server/query/change/AgePredicate.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import java.sql.Timestamp;
 
 public class AgePredicate extends TimestampRangeChangePredicate {
@@ -46,7 +45,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     Change change = object.change();
     return change != null && change.getLastUpdatedOn().getTime() <= cut;
   }
diff --git a/java/com/google/gerrit/server/query/change/AndChangeSource.java b/java/com/google/gerrit/server/query/change/AndChangeSource.java
index ff1ab23..4a3b936 100644
--- a/java/com/google/gerrit/server/query/change/AndChangeSource.java
+++ b/java/com/google/gerrit/server/query/change/AndChangeSource.java
@@ -17,8 +17,6 @@
 import com.google.gerrit.index.query.AndSource;
 import com.google.gerrit.index.query.IsVisibleToPredicate;
 import com.google.gerrit.index.query.Predicate;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
 import java.util.Collection;
 import java.util.List;
 
@@ -43,13 +41,9 @@
   }
 
   @Override
-  protected List<ChangeData> transformBuffer(List<ChangeData> buffer) throws OrmRuntimeException {
+  protected List<ChangeData> transformBuffer(List<ChangeData> buffer) {
     if (!hasChange()) {
-      try {
-        ChangeData.ensureChangeLoaded(buffer);
-      } catch (OrmException e) {
-        throw new OrmRuntimeException(e);
-      }
+      ChangeData.ensureChangeLoaded(buffer);
     }
     return super.transformBuffer(buffer);
   }
diff --git a/java/com/google/gerrit/server/query/change/AssigneePredicate.java b/java/com/google/gerrit/server/query/change/AssigneePredicate.java
index 63f7467..fb19e85 100644
--- a/java/com/google/gerrit/server/query/change/AssigneePredicate.java
+++ b/java/com/google/gerrit/server/query/change/AssigneePredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class AssigneePredicate extends ChangeIndexPredicate {
   protected final Account.Id id;
@@ -27,7 +26,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     if (id.get() == ChangeField.NO_ASSIGNEE) {
       Account.Id assignee = object.change().getAssignee();
       return assignee == null;
diff --git a/java/com/google/gerrit/server/query/change/AuthorPredicate.java b/java/com/google/gerrit/server/query/change/AuthorPredicate.java
index 3ee3352..79914a3 100644
--- a/java/com/google/gerrit/server/query/change/AuthorPredicate.java
+++ b/java/com/google/gerrit/server/query/change/AuthorPredicate.java
@@ -18,8 +18,6 @@
 import static com.google.gerrit.server.query.change.ChangeQueryBuilder.FIELD_AUTHOR;
 
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
 
 public class AuthorPredicate extends ChangeIndexPredicate {
   public AuthorPredicate(String value) {
@@ -27,12 +25,8 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
-    try {
-      return ChangeField.getAuthorParts(object).contains(getValue().toLowerCase());
-    } catch (IOException e) {
-      throw new OrmException(e);
-    }
+  public boolean match(ChangeData object) {
+    return ChangeField.getAuthorParts(object).contains(getValue().toLowerCase());
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/query/change/BeforePredicate.java b/java/com/google/gerrit/server/query/change/BeforePredicate.java
index 4d6ed69..dacabc0 100644
--- a/java/com/google/gerrit/server/query/change/BeforePredicate.java
+++ b/java/com/google/gerrit/server/query/change/BeforePredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 import java.util.Date;
 
 public class BeforePredicate extends TimestampRangeChangePredicate {
@@ -38,7 +37,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     return cd.change().getLastUpdatedOn().getTime() <= cut.getTime();
   }
 }
diff --git a/java/com/google/gerrit/server/query/change/BooleanPredicate.java b/java/com/google/gerrit/server/query/change/BooleanPredicate.java
index 5930b74..68f83e8 100644
--- a/java/com/google/gerrit/server/query/change/BooleanPredicate.java
+++ b/java/com/google/gerrit/server/query/change/BooleanPredicate.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.query.change;
 
 import com.google.gerrit.index.FieldDef;
-import com.google.gwtorm.server.OrmException;
 
 public class BooleanPredicate extends ChangeIndexPredicate {
   public BooleanPredicate(FieldDef<ChangeData, String> field) {
@@ -23,7 +22,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     return getValue().equals(getField().get(object));
   }
 
diff --git a/java/com/google/gerrit/server/query/change/ChangeData.java b/java/com/google/gerrit/server/query/change/ChangeData.java
index 5a300b4..619569f 100644
--- a/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -34,6 +34,7 @@
 import com.google.gerrit.common.data.LabelTypes;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -73,7 +74,6 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.project.SubmitRuleEvaluator;
 import com.google.gerrit.server.project.SubmitRuleOptions;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
@@ -89,9 +89,6 @@
 import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Stream;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
@@ -101,7 +98,7 @@
 import org.eclipse.jgit.revwalk.RevWalk;
 
 public class ChangeData {
-  public static List<Change> asChanges(List<ChangeData> changeDatas) throws OrmException {
+  public static List<Change> asChanges(List<ChangeData> changeDatas) {
     List<Change> result = new ArrayList<>(changeDatas.size());
     for (ChangeData cd : changeDatas) {
       result.add(cd.change());
@@ -113,7 +110,7 @@
     return changes.stream().collect(toMap(ChangeData::getId, Function.identity()));
   }
 
-  public static void ensureChangeLoaded(Iterable<ChangeData> changes) throws OrmException {
+  public static void ensureChangeLoaded(Iterable<ChangeData> changes) {
     ChangeData first = Iterables.getFirst(changes, null);
     if (first == null) {
       return;
@@ -124,7 +121,7 @@
     }
   }
 
-  public static void ensureAllPatchSetsLoaded(Iterable<ChangeData> changes) throws OrmException {
+  public static void ensureAllPatchSetsLoaded(Iterable<ChangeData> changes) {
     ChangeData first = Iterables.getFirst(changes, null);
     if (first == null) {
       return;
@@ -135,7 +132,7 @@
     }
   }
 
-  public static void ensureCurrentPatchSetLoaded(Iterable<ChangeData> changes) throws OrmException {
+  public static void ensureCurrentPatchSetLoaded(Iterable<ChangeData> changes) {
     ChangeData first = Iterables.getFirst(changes, null);
     if (first == null) {
       return;
@@ -146,8 +143,7 @@
     }
   }
 
-  public static void ensureCurrentApprovalsLoaded(Iterable<ChangeData> changes)
-      throws OrmException {
+  public static void ensureCurrentApprovalsLoaded(Iterable<ChangeData> changes) {
     ChangeData first = Iterables.getFirst(changes, null);
     if (first == null) {
       return;
@@ -158,7 +154,7 @@
     }
   }
 
-  public static void ensureMessagesLoaded(Iterable<ChangeData> changes) throws OrmException {
+  public static void ensureMessagesLoaded(Iterable<ChangeData> changes) {
     ChangeData first = Iterables.getFirst(changes, null);
     if (first == null) {
       return;
@@ -169,8 +165,7 @@
     }
   }
 
-  public static void ensureReviewedByLoadedForOpenChanges(Iterable<ChangeData> changes)
-      throws OrmException {
+  public static void ensureReviewedByLoadedForOpenChanges(Iterable<ChangeData> changes) {
     List<ChangeData> pending = new ArrayList<>();
     for (ChangeData cd : changes) {
       if (cd.reviewedBy == null && cd.change().isNew()) {
@@ -362,14 +357,14 @@
     return allUsersName;
   }
 
-  public void setCurrentFilePaths(List<String> filePaths) throws OrmException {
+  public void setCurrentFilePaths(List<String> filePaths) {
     PatchSet ps = currentPatchSet();
     if (ps != null) {
       currentFiles = ImmutableList.copyOf(filePaths);
     }
   }
 
-  public List<String> currentFilePaths() throws IOException, OrmException {
+  public List<String> currentFilePaths() {
     if (currentFiles == null) {
       if (!lazyLoad) {
         return Collections.emptyList();
@@ -380,7 +375,7 @@
     return currentFiles;
   }
 
-  private Optional<DiffSummary> getDiffSummary() throws OrmException, IOException {
+  private Optional<DiffSummary> getDiffSummary() {
     if (diffSummary == null) {
       if (!lazyLoad) {
         return Optional.empty();
@@ -408,7 +403,7 @@
     return diffSummary;
   }
 
-  private Optional<ChangedLines> computeChangedLines() throws OrmException, IOException {
+  private Optional<ChangedLines> computeChangedLines() {
     Optional<DiffSummary> ds = getDiffSummary();
     if (ds.isPresent()) {
       return Optional.of(ds.get().getChangedLines());
@@ -416,7 +411,7 @@
     return Optional.empty();
   }
 
-  public Optional<ChangedLines> changedLines() throws OrmException, IOException {
+  public Optional<ChangedLines> changedLines() {
     if (changedLines == null) {
       if (!lazyLoad) {
         return Optional.empty();
@@ -450,7 +445,7 @@
     visibleTo = user;
   }
 
-  public Change change() throws OrmException {
+  public Change change() {
     if (change == null && lazyLoad) {
       reloadChange();
     }
@@ -461,41 +456,41 @@
     change = c;
   }
 
-  public Change reloadChange() throws OrmException {
+  public Change reloadChange() {
     try {
       notes = notesFactory.createChecked(project, legacyId);
     } catch (NoSuchChangeException e) {
-      throw new OrmException("Unable to load change " + legacyId, e);
+      throw new StorageException("Unable to load change " + legacyId, e);
     }
     change = notes.getChange();
     setPatchSets(null);
     return change;
   }
 
-  public LabelTypes getLabelTypes() throws OrmException {
+  public LabelTypes getLabelTypes() {
     if (labelTypes == null) {
       ProjectState state;
       try {
         state = projectCache.checkedGet(project());
       } catch (IOException e) {
-        throw new OrmException("project state not available", e);
+        throw new StorageException("project state not available", e);
       }
       labelTypes = state.getLabelTypes(change().getDest());
     }
     return labelTypes;
   }
 
-  public ChangeNotes notes() throws OrmException {
+  public ChangeNotes notes() {
     if (notes == null) {
       if (!lazyLoad) {
-        throw new OrmException("ChangeNotes not available, lazyLoad = false");
+        throw new StorageException("ChangeNotes not available, lazyLoad = false");
       }
       notes = notesFactory.create(project(), legacyId);
     }
     return notes;
   }
 
-  public PatchSet currentPatchSet() throws OrmException {
+  public PatchSet currentPatchSet() {
     if (currentPatchSet == null) {
       Change c = change();
       if (c == null) {
@@ -511,7 +506,7 @@
     return currentPatchSet;
   }
 
-  public List<PatchSetApproval> currentApprovals() throws OrmException {
+  public List<PatchSetApproval> currentApprovals() {
     if (currentApprovals == null) {
       if (!lazyLoad) {
         return Collections.emptyList();
@@ -524,7 +519,7 @@
           currentApprovals =
               ImmutableList.copyOf(
                   approvalsUtil.byPatchSet(notes(), c.currentPatchSetId(), null, null));
-        } catch (OrmException e) {
+        } catch (StorageException e) {
           if (e.getCause() instanceof NoSuchChangeException) {
             currentApprovals = Collections.emptyList();
           } else {
@@ -540,7 +535,7 @@
     currentApprovals = approvals;
   }
 
-  public String commitMessage() throws IOException, OrmException {
+  public String commitMessage() {
     if (commitMessage == null) {
       if (!loadCommitData()) {
         return null;
@@ -549,7 +544,7 @@
     return commitMessage;
   }
 
-  public List<FooterLine> commitFooters() throws IOException, OrmException {
+  public List<FooterLine> commitFooters() {
     if (commitFooters == null) {
       if (!loadCommitData()) {
         return null;
@@ -558,11 +553,11 @@
     return commitFooters;
   }
 
-  public ListMultimap<String, String> trackingFooters() throws IOException, OrmException {
+  public ListMultimap<String, String> trackingFooters() {
     return trackingFooters.extract(commitFooters());
   }
 
-  public PersonIdent getAuthor() throws IOException, OrmException {
+  public PersonIdent getAuthor() {
     if (author == null) {
       if (!loadCommitData()) {
         return null;
@@ -571,7 +566,7 @@
     return author;
   }
 
-  public PersonIdent getCommitter() throws IOException, OrmException {
+  public PersonIdent getCommitter() {
     if (committer == null) {
       if (!loadCommitData()) {
         return null;
@@ -580,9 +575,7 @@
     return committer;
   }
 
-  private boolean loadCommitData()
-      throws OrmException, RepositoryNotFoundException, IOException, MissingObjectException,
-          IncorrectObjectTypeException {
+  private boolean loadCommitData() {
     PatchSet ps = currentPatchSet();
     if (ps == null) {
       return false;
@@ -596,15 +589,14 @@
       author = c.getAuthorIdent();
       committer = c.getCommitterIdent();
       parentCount = c.getParentCount();
+    } catch (IOException e) {
+      throw new StorageException(e);
     }
     return true;
   }
 
-  /**
-   * @return patches for the change, in patch set ID order.
-   * @throws OrmException an error occurred reading the database.
-   */
-  public Collection<PatchSet> patchSets() throws OrmException {
+  /** @return patches for the change, in patch set ID order. */
+  public Collection<PatchSet> patchSets() {
     if (patchSets == null) {
       patchSets = psUtil.byChange(notes());
     }
@@ -616,11 +608,8 @@
     this.patchSets = patchSets;
   }
 
-  /**
-   * @return patch with the given ID, or null if it does not exist.
-   * @throws OrmException an error occurred reading the database.
-   */
-  public PatchSet patchSet(PatchSet.Id psId) throws OrmException {
+  /** @return patch with the given ID, or null if it does not exist. */
+  public PatchSet patchSet(PatchSet.Id psId) {
     if (currentPatchSet != null && currentPatchSet.getId().equals(psId)) {
       return currentPatchSet;
     }
@@ -635,9 +624,8 @@
   /**
    * @return all patch set approvals for the change, keyed by ID, ordered by timestamp within each
    *     patch set.
-   * @throws OrmException an error occurred reading the database.
    */
-  public ListMultimap<PatchSet.Id, PatchSetApproval> approvals() throws OrmException {
+  public ListMultimap<PatchSet.Id, PatchSetApproval> approvals() {
     if (allApprovals == null) {
       if (!lazyLoad) {
         return ImmutableListMultimap.of();
@@ -647,15 +635,12 @@
     return allApprovals;
   }
 
-  /**
-   * @return The submit ('SUBM') approval label
-   * @throws OrmException an error occurred reading the database.
-   */
-  public Optional<PatchSetApproval> getSubmitApproval() throws OrmException {
+  /** @return The submit ('SUBM') approval label */
+  public Optional<PatchSetApproval> getSubmitApproval() {
     return currentApprovals().stream().filter(PatchSetApproval::isLegacySubmit).findFirst();
   }
 
-  public ReviewerSet reviewers() throws OrmException {
+  public ReviewerSet reviewers() {
     if (reviewers == null) {
       if (!lazyLoad) {
         return ReviewerSet.empty();
@@ -673,7 +658,7 @@
     return reviewers;
   }
 
-  public ReviewerByEmailSet reviewersByEmail() throws OrmException {
+  public ReviewerByEmailSet reviewersByEmail() {
     if (reviewersByEmail == null) {
       if (!lazyLoad) {
         return ReviewerByEmailSet.empty();
@@ -699,7 +684,7 @@
     return this.pendingReviewers;
   }
 
-  public ReviewerSet pendingReviewers() throws OrmException {
+  public ReviewerSet pendingReviewers() {
     if (pendingReviewers == null) {
       if (!lazyLoad) {
         return ReviewerSet.empty();
@@ -717,7 +702,7 @@
     return pendingReviewersByEmail;
   }
 
-  public ReviewerByEmailSet pendingReviewersByEmail() throws OrmException {
+  public ReviewerByEmailSet pendingReviewersByEmail() {
     if (pendingReviewersByEmail == null) {
       if (!lazyLoad) {
         return ReviewerByEmailSet.empty();
@@ -727,7 +712,7 @@
     return pendingReviewersByEmail;
   }
 
-  public List<ReviewerStatusUpdate> reviewerUpdates() throws OrmException {
+  public List<ReviewerStatusUpdate> reviewerUpdates() {
     if (reviewerUpdates == null) {
       if (!lazyLoad) {
         return Collections.emptyList();
@@ -745,7 +730,7 @@
     return reviewerUpdates;
   }
 
-  public Collection<Comment> publishedComments() throws OrmException {
+  public Collection<Comment> publishedComments() {
     if (publishedComments == null) {
       if (!lazyLoad) {
         return Collections.emptyList();
@@ -755,7 +740,7 @@
     return publishedComments;
   }
 
-  public Collection<RobotComment> robotComments() throws OrmException {
+  public Collection<RobotComment> robotComments() {
     if (robotComments == null) {
       if (!lazyLoad) {
         return Collections.emptyList();
@@ -765,7 +750,7 @@
     return robotComments;
   }
 
-  public Integer unresolvedCommentCount() throws OrmException {
+  public Integer unresolvedCommentCount() {
     if (unresolvedCommentCount == null) {
       if (!lazyLoad) {
         return null;
@@ -819,7 +804,7 @@
     this.unresolvedCommentCount = count;
   }
 
-  public Integer totalCommentCount() throws OrmException {
+  public Integer totalCommentCount() {
     if (totalCommentCount == null) {
       if (!lazyLoad) {
         return null;
@@ -836,7 +821,7 @@
     this.totalCommentCount = count;
   }
 
-  public List<ChangeMessage> messages() throws OrmException {
+  public List<ChangeMessage> messages() {
     if (messages == null) {
       if (!lazyLoad) {
         return Collections.emptyList();
@@ -880,7 +865,7 @@
   }
 
   @Nullable
-  public Boolean isMergeable() throws OrmException {
+  public Boolean isMergeable() {
     if (mergeable == null) {
       Change c = change();
       if (c == null) {
@@ -920,18 +905,18 @@
                   c.getDest(),
                   repo);
         } catch (IOException e) {
-          throw new OrmException(e);
+          throw new StorageException(e);
         }
       }
     }
     return mergeable;
   }
 
-  public Set<Account.Id> editsByUser() throws OrmException {
+  public Set<Account.Id> editsByUser() {
     return editRefs().keySet();
   }
 
-  public Map<Account.Id, Ref> editRefs() throws OrmException {
+  public Map<Account.Id, Ref> editRefs() {
     if (editsByUser == null) {
       if (!lazyLoad) {
         return Collections.emptyMap();
@@ -953,17 +938,17 @@
           }
         }
       } catch (IOException e) {
-        throw new OrmException(e);
+        throw new StorageException(e);
       }
     }
     return editsByUser;
   }
 
-  public Set<Account.Id> draftsByUser() throws OrmException {
+  public Set<Account.Id> draftsByUser() {
     return draftRefs().keySet();
   }
 
-  public Map<Account.Id, Ref> draftRefs() throws OrmException {
+  public Map<Account.Id, Ref> draftRefs() {
     if (draftsByUser == null) {
       if (!lazyLoad) {
         return Collections.emptyMap();
@@ -991,7 +976,7 @@
     return draftsByUser;
   }
 
-  public boolean isReviewedBy(Account.Id accountId) throws OrmException {
+  public boolean isReviewedBy(Account.Id accountId) {
     Collection<String> stars = stars(accountId);
 
     PatchSet ps = currentPatchSet();
@@ -1008,7 +993,7 @@
     return reviewedBy().contains(accountId);
   }
 
-  public Set<Account.Id> reviewedBy() throws OrmException {
+  public Set<Account.Id> reviewedBy() {
     if (reviewedBy == null) {
       if (!lazyLoad) {
         return Collections.emptySet();
@@ -1040,7 +1025,7 @@
     this.reviewedBy = reviewedBy;
   }
 
-  public Set<String> hashtags() throws OrmException {
+  public Set<String> hashtags() {
     if (hashtags == null) {
       if (!lazyLoad) {
         return Collections.emptySet();
@@ -1054,7 +1039,7 @@
     this.hashtags = hashtags;
   }
 
-  public ImmutableListMultimap<Account.Id, String> stars() throws OrmException {
+  public ImmutableListMultimap<Account.Id, String> stars() {
     if (stars == null) {
       if (!lazyLoad) {
         return ImmutableListMultimap.of();
@@ -1072,7 +1057,7 @@
     this.stars = ImmutableListMultimap.copyOf(stars);
   }
 
-  public ImmutableMap<Account.Id, StarRef> starRefs() throws OrmException {
+  public ImmutableMap<Account.Id, StarRef> starRefs() {
     if (starRefs == null) {
       if (!lazyLoad) {
         return ImmutableMap.of();
@@ -1082,7 +1067,7 @@
     return starRefs;
   }
 
-  public Set<String> stars(Account.Id accountId) throws OrmException {
+  public Set<String> stars(Account.Id accountId) {
     if (starsOf != null) {
       if (!starsOf.accountId().equals(accountId)) {
         starsOf = null;
@@ -1106,14 +1091,14 @@
    *     false otherwise.
    */
   @Nullable
-  public Boolean isPureRevert() throws OrmException {
+  public Boolean isPureRevert() {
     if (change().getRevertOf() == null) {
       return null;
     }
     try {
       return pureRevert.get(notes(), Optional.empty());
     } catch (IOException | BadRequestException | ResourceConflictException e) {
-      throw new OrmException("could not compute pure revert", e);
+      throw new StorageException("could not compute pure revert", e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java b/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java
index d541d18..74ad0ef 100644
--- a/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ChangeIdPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 /** Predicate over Change-Id strings (aka Change.Key). */
 public class ChangeIdPredicate extends ChangeIndexPredicate {
@@ -25,7 +24,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     Change change = cd.change();
     if (change == null) {
       return false;
diff --git a/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java b/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
index a793cf2..60b4d38 100644
--- a/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.query.change;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.index.query.IsVisibleToPredicate;
 import com.google.gerrit.reviewdb.client.Change;
@@ -27,7 +28,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -58,7 +58,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     if (cd.fastIsVisibleTo(user)) {
       return true;
     }
@@ -80,7 +80,7 @@
         return false;
       }
     } catch (IOException e) {
-      throw new OrmException("unable to read project state", e);
+      throw new StorageException("unable to read project state", e);
     }
 
     PermissionBackend.WithUser withUser =
@@ -97,7 +97,7 @@
             cd, cd.project());
         return false;
       }
-      throw new OrmException("unable to check permissions on change " + cd.getId(), e);
+      throw new StorageException("unable to check permissions on change " + cd.getId(), e);
     } catch (AuthException e) {
       logger.atFine().log("Filter out non-visisble change: %s", cd);
       return false;
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 0aa4c2d..0227153 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -31,9 +31,9 @@
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.SubmitRecord;
-import com.google.gerrit.common.errors.NotSignedInException;
+import com.google.gerrit.exceptions.NotSignedInException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.registration.Extension;
 import com.google.gerrit.index.IndexConfig;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.SchemaUtil;
@@ -76,7 +76,6 @@
 import com.google.gerrit.server.project.ChildProjects;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.submit.SubmitDryRun;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.ProvisionException;
@@ -98,7 +97,7 @@
 import org.eclipse.jgit.lib.Repository;
 
 /** Parses a query string meant to be applied to change objects. */
-public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
+public class ChangeQueryBuilder extends QueryBuilder<ChangeData, ChangeQueryBuilder> {
   public interface ChangeOperatorFactory extends OperatorFactory<ChangeData, ChangeQueryBuilder> {}
 
   /**
@@ -408,25 +407,15 @@
 
   @Inject
   ChangeQueryBuilder(Arguments args) {
-    super(mydef);
-    this.args = args;
-    setupDynamicOperators();
+    this(mydef, args);
   }
 
   @VisibleForTesting
-  protected ChangeQueryBuilder(
-      Definition<ChangeData, ? extends QueryBuilder<ChangeData>> def, Arguments args) {
-    super(def);
+  protected ChangeQueryBuilder(Definition<ChangeData, ChangeQueryBuilder> def, Arguments args) {
+    super(def, args.opFactories);
     this.args = args;
   }
 
-  private void setupDynamicOperators() {
-    for (Extension<ChangeOperatorFactory> e : args.opFactories) {
-      String name = e.getExportName() + "_" + e.getPluginName();
-      opFactories.put(name, e.getProvider().get());
-    }
-  }
-
   public Arguments getArgs() {
     return args;
   }
@@ -619,7 +608,7 @@
   }
 
   @Operator
-  public Predicate<ChangeData> conflicts(String value) throws OrmException, QueryParseException {
+  public Predicate<ChangeData> conflicts(String value) throws QueryParseException {
     List<Change> changes = parseChange(value);
     List<Predicate<ChangeData>> or = new ArrayList<>(changes.size());
     for (Change c : changes) {
@@ -793,7 +782,7 @@
 
   @Operator
   public Predicate<ChangeData> label(String name)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     Set<Account.Id> accounts = null;
     AccountGroup.UUID group = null;
 
@@ -893,7 +882,7 @@
 
   @Operator
   public Predicate<ChangeData> starredby(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     return starredby(parseAccount(who));
   }
 
@@ -911,7 +900,7 @@
 
   @Operator
   public Predicate<ChangeData> watchedby(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     Set<Account.Id> m = parseAccount(who);
     List<IsWatchedByPredicate> p = Lists.newArrayListWithCapacity(m.size());
 
@@ -936,7 +925,7 @@
 
   @Operator
   public Predicate<ChangeData> draftby(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     Set<Account.Id> m = parseAccount(who);
     List<Predicate<ChangeData>> p = Lists.newArrayListWithCapacity(m.size());
     for (Account.Id id : m) {
@@ -951,7 +940,7 @@
 
   @Operator
   public Predicate<ChangeData> visibleto(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     if (isSelf(who)) {
       return is_visible();
     }
@@ -994,13 +983,13 @@
 
   @Operator
   public Predicate<ChangeData> o(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     return owner(who);
   }
 
   @Operator
   public Predicate<ChangeData> owner(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     return owner(parseAccount(who));
   }
 
@@ -1013,7 +1002,7 @@
   }
 
   private Predicate<ChangeData> ownerDefaultField(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     Set<Account.Id> accounts = parseAccount(who);
     if (accounts.size() > MAX_ACCOUNTS_PER_DEFAULT_FIELD) {
       return Predicate.any();
@@ -1023,7 +1012,7 @@
 
   @Operator
   public Predicate<ChangeData> assignee(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     return assignee(parseAccount(who));
   }
 
@@ -1058,23 +1047,23 @@
 
   @Operator
   public Predicate<ChangeData> r(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     return reviewer(who);
   }
 
   @Operator
   public Predicate<ChangeData> reviewer(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     return reviewer(who, false);
   }
 
   private Predicate<ChangeData> reviewerDefaultField(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     return reviewer(who, true);
   }
 
   private Predicate<ChangeData> reviewer(String who, boolean forDefaultField)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     Predicate<ChangeData> byState =
         reviewerByState(who, ReviewerStateInternal.REVIEWER, forDefaultField);
     if (Objects.equals(byState, Predicate.<ChangeData>any())) {
@@ -1088,7 +1077,7 @@
 
   @Operator
   public Predicate<ChangeData> cc(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     return reviewerByState(who, ReviewerStateInternal.CC, false);
   }
 
@@ -1142,7 +1131,7 @@
 
   @Operator
   public Predicate<ChangeData> commentby(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     return commentby(parseAccount(who));
   }
 
@@ -1156,7 +1145,7 @@
 
   @Operator
   public Predicate<ChangeData> from(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     Set<Account.Id> ownerIds = parseAccount(who);
     return Predicate.or(owner(ownerIds), commentby(ownerIds));
   }
@@ -1181,7 +1170,7 @@
 
   @Operator
   public Predicate<ChangeData> reviewedby(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     return IsReviewedPredicate.create(parseAccount(who));
   }
 
@@ -1271,7 +1260,7 @@
       if (!Objects.equals(p, Predicate.<ChangeData>any())) {
         predicates.add(p);
       }
-    } catch (OrmException | IOException | ConfigInvalidException | QueryParseException e) {
+    } catch (StorageException | IOException | ConfigInvalidException | QueryParseException e) {
       // Skip.
     }
     try {
@@ -1279,13 +1268,13 @@
       if (!Objects.equals(p, Predicate.<ChangeData>any())) {
         predicates.add(p);
       }
-    } catch (OrmException | IOException | ConfigInvalidException | QueryParseException e) {
+    } catch (StorageException | IOException | ConfigInvalidException | QueryParseException e) {
       // Skip.
     }
     predicates.add(file(query));
     try {
       predicates.add(label(query));
-    } catch (OrmException | IOException | ConfigInvalidException | QueryParseException e) {
+    } catch (StorageException | IOException | ConfigInvalidException | QueryParseException e) {
       // Skip.
     }
     predicates.add(commit(query));
@@ -1339,7 +1328,7 @@
   }
 
   private Set<Account.Id> parseAccount(String who)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     try {
       return args.accountResolver.resolve(who).asNonEmptyIdSet();
     } catch (UnresolvableAccountException e) {
@@ -1358,7 +1347,7 @@
     return g;
   }
 
-  private List<Change> parseChange(String value) throws OrmException, QueryParseException {
+  private List<Change> parseChange(String value) throws QueryParseException {
     if (PAT_LEGACY_ID.matcher(value).matches()) {
       return asChanges(args.queryProvider.get().byLegacyChangeId(Change.Id.parse(value)));
     } else if (PAT_CHANGE_ID.matcher(value).matches()) {
@@ -1385,7 +1374,7 @@
 
   public Predicate<ChangeData> reviewerByState(
       String who, ReviewerStateInternal state, boolean forDefaultField)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     Predicate<ChangeData> reviewerByEmailPredicate = null;
     if (args.index.getSchema().hasField(ChangeField.REVIEWER_BY_EMAIL)) {
       Address address = Address.tryParse(who);
diff --git a/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java b/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
index 5a7efb8..66790e7 100644
--- a/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Change.Status;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -119,7 +118,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     Change change = object.change();
     return change != null && Objects.equals(status, change.getStatus());
   }
diff --git a/java/com/google/gerrit/server/query/change/CommentByPredicate.java b/java/com/google/gerrit/server/query/change/CommentByPredicate.java
index 7ad7afe..0747bb2 100644
--- a/java/com/google/gerrit/server/query/change/CommentByPredicate.java
+++ b/java/com/google/gerrit/server/query/change/CommentByPredicate.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.reviewdb.client.ChangeMessage;
 import com.google.gerrit.reviewdb.client.Comment;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 import java.util.Objects;
 
 public class CommentByPredicate extends ChangeIndexPredicate {
@@ -34,7 +33,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     for (ChangeMessage m : cd.messages()) {
       if (Objects.equals(m.getAuthor(), id)) {
         return true;
diff --git a/java/com/google/gerrit/server/query/change/CommentPredicate.java b/java/com/google/gerrit/server/query/change/CommentPredicate.java
index 5a6d186..d193bb6 100644
--- a/java/com/google/gerrit/server/query/change/CommentPredicate.java
+++ b/java/com/google/gerrit/server/query/change/CommentPredicate.java
@@ -14,12 +14,12 @@
 
 package com.google.gerrit.server.query.change;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.index.change.ChangeIndex;
 import com.google.gerrit.server.index.change.IndexedChangeQuery;
-import com.google.gwtorm.server.OrmException;
 
 public class CommentPredicate extends ChangeIndexPredicate {
   protected final ChangeIndex index;
@@ -30,7 +30,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     try {
       Predicate<ChangeData> p = Predicate.and(new LegacyChangeIdPredicate(object.getId()), this);
       for (ChangeData cData : index.getSource(p, IndexedChangeQuery.oneResult()).read()) {
@@ -39,7 +39,7 @@
         }
       }
     } catch (QueryParseException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
 
     return false;
diff --git a/java/com/google/gerrit/server/query/change/CommitPredicate.java b/java/com/google/gerrit/server/query/change/CommitPredicate.java
index d1ae529..567f58d 100644
--- a/java/com/google/gerrit/server/query/change/CommitPredicate.java
+++ b/java/com/google/gerrit/server/query/change/CommitPredicate.java
@@ -20,7 +20,6 @@
 
 import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gwtorm.server.OrmException;
 
 public class CommitPredicate extends ChangeIndexPredicate {
   static FieldDef<ChangeData, ?> commitField(String id) {
@@ -35,7 +34,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     String id = getValue().toLowerCase();
     for (PatchSet p : object.patchSets()) {
       if (equals(p, id)) {
diff --git a/java/com/google/gerrit/server/query/change/CommitterPredicate.java b/java/com/google/gerrit/server/query/change/CommitterPredicate.java
index 797cb9d..1dcf97f 100644
--- a/java/com/google/gerrit/server/query/change/CommitterPredicate.java
+++ b/java/com/google/gerrit/server/query/change/CommitterPredicate.java
@@ -18,8 +18,6 @@
 import static com.google.gerrit.server.query.change.ChangeQueryBuilder.FIELD_COMMITTER;
 
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
 
 public class CommitterPredicate extends ChangeIndexPredicate {
   public CommitterPredicate(String value) {
@@ -27,12 +25,8 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
-    try {
-      return ChangeField.getCommitterParts(object).contains(getValue().toLowerCase());
-    } catch (IOException e) {
-      throw new OrmException(e);
-    }
+  public boolean match(ChangeData object) {
+    return ChangeField.getCommitterParts(object).contains(getValue().toLowerCase());
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/query/change/ConflictsPredicate.java b/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
index ada9f27..d415f71 100644
--- a/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
@@ -20,6 +20,7 @@
 
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.query.PostFilterPredicate;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
@@ -35,7 +36,6 @@
 import com.google.gerrit.server.query.change.ChangeQueryBuilder.Arguments;
 import com.google.gerrit.server.submit.IntegrationException;
 import com.google.gerrit.server.submit.SubmitDryRun;
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -61,7 +61,7 @@
     try {
       cd = args.changeDataFactory.create(c);
       files = cd.currentFilePaths();
-    } catch (IOException | OrmException e) {
+    } catch (StorageException e) {
       warnWithOccasionalStackTrace(
           e,
           "Error constructing conflicts predicates for change %s in %s",
@@ -158,7 +158,7 @@
           args.conflictsCache.put(conflictsKey, conflicts);
           return conflicts;
         }
-      } catch (IntegrationException | NoSuchProjectException | OrmException | IOException e) {
+      } catch (IntegrationException | NoSuchProjectException | StorageException | IOException e) {
         ObjectId finalOther = other;
         warnWithOccasionalStackTrace(
             e,
@@ -186,7 +186,7 @@
           accepted.add(rw.parseCommit(tip));
         }
         return accepted;
-      } catch (OrmException | IOException e) {
+      } catch (StorageException | IOException e) {
         throw new IntegrationException("Failed to determine already accepted commits.", e);
       }
     }
@@ -205,7 +205,7 @@
       this.projectCache = projectCache;
     }
 
-    ObjectId getTestAgainst() throws OrmException {
+    ObjectId getTestAgainst() {
       if (testAgainst == null) {
         testAgainst = ObjectId.fromString(cd.currentPatchSet().getRevision().get());
       }
diff --git a/java/com/google/gerrit/server/query/change/DeletedPredicate.java b/java/com/google/gerrit/server/query/change/DeletedPredicate.java
index 6232fc5..d4bdc67 100644
--- a/java/com/google/gerrit/server/query/change/DeletedPredicate.java
+++ b/java/com/google/gerrit/server/query/change/DeletedPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class DeletedPredicate extends IntegerRangeChangePredicate {
   public DeletedPredicate(String value) throws QueryParseException {
@@ -24,7 +23,7 @@
   }
 
   @Override
-  protected Integer getValueInt(ChangeData changeData) throws OrmException {
+  protected Integer getValueInt(ChangeData changeData) {
     return ChangeField.DELETED.get(changeData);
   }
 }
diff --git a/java/com/google/gerrit/server/query/change/DeltaPredicate.java b/java/com/google/gerrit/server/query/change/DeltaPredicate.java
index aae0a20..821ec94 100644
--- a/java/com/google/gerrit/server/query/change/DeltaPredicate.java
+++ b/java/com/google/gerrit/server/query/change/DeltaPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class DeltaPredicate extends IntegerRangeChangePredicate {
   public DeltaPredicate(String value) throws QueryParseException {
@@ -24,7 +23,7 @@
   }
 
   @Override
-  protected Integer getValueInt(ChangeData changeData) throws OrmException {
+  protected Integer getValueInt(ChangeData changeData) {
     return ChangeField.DELTA.get(changeData);
   }
 }
diff --git a/java/com/google/gerrit/server/query/change/DestinationPredicate.java b/java/com/google/gerrit/server/query/change/DestinationPredicate.java
index a824a87..702745e 100644
--- a/java/com/google/gerrit/server/query/change/DestinationPredicate.java
+++ b/java/com/google/gerrit/server/query/change/DestinationPredicate.java
@@ -17,7 +17,6 @@
 import com.google.gerrit.index.query.PostFilterPredicate;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwtorm.server.OrmException;
 import java.util.Set;
 
 public class DestinationPredicate extends PostFilterPredicate<ChangeData> {
@@ -29,7 +28,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     Change change = object.change();
     if (change == null) {
       return false;
diff --git a/java/com/google/gerrit/server/query/change/DirectoryPredicate.java b/java/com/google/gerrit/server/query/change/DirectoryPredicate.java
index 676a208..3ab3e26 100644
--- a/java/com/google/gerrit/server/query/change/DirectoryPredicate.java
+++ b/java/com/google/gerrit/server/query/change/DirectoryPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.common.base.CharMatcher;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 import java.util.Locale;
 
 public class DirectoryPredicate extends ChangeIndexPredicate {
@@ -29,7 +28,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     return ChangeField.getDirectories(cd).contains(value);
   }
 
diff --git a/java/com/google/gerrit/server/query/change/EditByPredicate.java b/java/com/google/gerrit/server/query/change/EditByPredicate.java
index 3238dc9..dfe7310 100644
--- a/java/com/google/gerrit/server/query/change/EditByPredicate.java
+++ b/java/com/google/gerrit/server/query/change/EditByPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class EditByPredicate extends ChangeIndexPredicate {
   protected final Account.Id id;
@@ -27,7 +26,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     return cd.editsByUser().contains(id);
   }
 
diff --git a/java/com/google/gerrit/server/query/change/EqualsFilePredicate.java b/java/com/google/gerrit/server/query/change/EqualsFilePredicate.java
index b5a2d05..9c033b6 100644
--- a/java/com/google/gerrit/server/query/change/EqualsFilePredicate.java
+++ b/java/com/google/gerrit/server/query/change/EqualsFilePredicate.java
@@ -17,7 +17,6 @@
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.query.change.ChangeQueryBuilder.Arguments;
-import com.google.gwtorm.server.OrmException;
 
 public class EqualsFilePredicate extends ChangeIndexPredicate {
   public static Predicate<ChangeData> create(Arguments args, String value) {
@@ -33,7 +32,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     return ChangeField.getFileParts(object).contains(value);
   }
 
diff --git a/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java b/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
index 430d8c3..acf2e25 100644
--- a/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
+++ b/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
@@ -28,7 +28,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 
 public class EqualsLabelPredicate extends ChangeIndexPredicate {
@@ -53,7 +52,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     Change c = object.change();
     if (c == null) {
       // The change has disappeared.
diff --git a/java/com/google/gerrit/server/query/change/EqualsPathPredicate.java b/java/com/google/gerrit/server/query/change/EqualsPathPredicate.java
index fc00283..76936fa 100644
--- a/java/com/google/gerrit/server/query/change/EqualsPathPredicate.java
+++ b/java/com/google/gerrit/server/query/change/EqualsPathPredicate.java
@@ -15,10 +15,7 @@
 package com.google.gerrit.server.query.change;
 
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
 import java.util.Collections;
-import java.util.List;
 
 public class EqualsPathPredicate extends ChangeIndexPredicate {
   public EqualsPathPredicate(String fieldName, String value) {
@@ -26,14 +23,8 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
-    List<String> files;
-    try {
-      files = object.currentFilePaths();
-    } catch (IOException e) {
-      throw new OrmException(e);
-    }
-    return Collections.binarySearch(files, value) >= 0;
+  public boolean match(ChangeData object) {
+    return Collections.binarySearch(object.currentFilePaths(), value) >= 0;
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/query/change/ExactAuthorPredicate.java b/java/com/google/gerrit/server/query/change/ExactAuthorPredicate.java
index bca5d3b..c1b6928 100644
--- a/java/com/google/gerrit/server/query/change/ExactAuthorPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ExactAuthorPredicate.java
@@ -18,8 +18,6 @@
 import static com.google.gerrit.server.query.change.ChangeQueryBuilder.FIELD_EXACTAUTHOR;
 
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
 import java.util.Locale;
 
 public class ExactAuthorPredicate extends ChangeIndexPredicate {
@@ -28,12 +26,8 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
-    try {
-      return ChangeField.getAuthorNameAndEmail(object).contains(getValue());
-    } catch (IOException e) {
-      throw new OrmException(e);
-    }
+  public boolean match(ChangeData object) {
+    return ChangeField.getAuthorNameAndEmail(object).contains(getValue());
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/query/change/ExactCommitterPredicate.java b/java/com/google/gerrit/server/query/change/ExactCommitterPredicate.java
index 3fae5e5..dac63af 100644
--- a/java/com/google/gerrit/server/query/change/ExactCommitterPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ExactCommitterPredicate.java
@@ -18,8 +18,6 @@
 import static com.google.gerrit.server.query.change.ChangeQueryBuilder.FIELD_EXACTCOMMITTER;
 
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
 import java.util.Locale;
 
 public class ExactCommitterPredicate extends ChangeIndexPredicate {
@@ -28,12 +26,8 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
-    try {
-      return ChangeField.getCommitterNameAndEmail(object).contains(getValue());
-    } catch (IOException e) {
-      throw new OrmException(e);
-    }
+  public boolean match(ChangeData object) {
+    return ChangeField.getCommitterNameAndEmail(object).contains(getValue());
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java b/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java
index 138cce5..c6ade75e 100644
--- a/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ExactTopicPredicate.java
@@ -17,7 +17,6 @@
 import static com.google.gerrit.server.index.change.ChangeField.EXACT_TOPIC;
 
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwtorm.server.OrmException;
 
 public class ExactTopicPredicate extends ChangeIndexPredicate {
   public ExactTopicPredicate(String topic) {
@@ -25,7 +24,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     Change change = object.change();
     if (change == null) {
       return false;
diff --git a/java/com/google/gerrit/server/query/change/FileExtensionListPredicate.java b/java/com/google/gerrit/server/query/change/FileExtensionListPredicate.java
index fc33b65..bddd2ec 100644
--- a/java/com/google/gerrit/server/query/change/FileExtensionListPredicate.java
+++ b/java/com/google/gerrit/server/query/change/FileExtensionListPredicate.java
@@ -18,7 +18,6 @@
 
 import com.google.common.base.Splitter;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class FileExtensionListPredicate extends ChangeIndexPredicate {
   private static String clean(String extList) {
@@ -34,7 +33,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     return ChangeField.getAllExtensionsAsList(cd).equals(value);
   }
 
diff --git a/java/com/google/gerrit/server/query/change/FileExtensionPredicate.java b/java/com/google/gerrit/server/query/change/FileExtensionPredicate.java
index 5353f11..ee573a7 100644
--- a/java/com/google/gerrit/server/query/change/FileExtensionPredicate.java
+++ b/java/com/google/gerrit/server/query/change/FileExtensionPredicate.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.query.change;
 
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 import java.util.Locale;
 
 public class FileExtensionPredicate extends ChangeIndexPredicate {
@@ -31,7 +30,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     return ChangeField.getExtensions(object).contains(value);
   }
 
diff --git a/java/com/google/gerrit/server/query/change/FooterPredicate.java b/java/com/google/gerrit/server/query/change/FooterPredicate.java
index 1d7d19b..4d7588c 100644
--- a/java/com/google/gerrit/server/query/change/FooterPredicate.java
+++ b/java/com/google/gerrit/server/query/change/FooterPredicate.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.query.change;
 
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 import java.util.Locale;
 
 public class FooterPredicate extends ChangeIndexPredicate {
@@ -35,7 +34,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     return ChangeField.getFooters(cd).contains(value);
   }
 
diff --git a/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java b/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
index 545b668..140f26b 100644
--- a/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
+++ b/java/com/google/gerrit/server/query/change/FuzzyTopicPredicate.java
@@ -17,12 +17,12 @@
 import static com.google.gerrit.server.index.change.ChangeField.FUZZY_TOPIC;
 
 import com.google.common.collect.Iterables;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.index.change.ChangeIndex;
 import com.google.gerrit.server.index.change.IndexedChangeQuery;
-import com.google.gwtorm.server.OrmException;
 
 public class FuzzyTopicPredicate extends ChangeIndexPredicate {
   protected final ChangeIndex index;
@@ -33,7 +33,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     Change change = cd.change();
     if (change == null) {
       return false;
@@ -48,7 +48,7 @@
           index.getSource(and(thisId, this), IndexedChangeQuery.oneResult()).read();
       return !Iterables.isEmpty(results);
     } catch (QueryParseException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/query/change/GroupPredicate.java b/java/com/google/gerrit/server/query/change/GroupPredicate.java
index d2645dc..7f7bcff 100644
--- a/java/com/google/gerrit/server/query/change/GroupPredicate.java
+++ b/java/com/google/gerrit/server/query/change/GroupPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 import java.util.List;
 
 public class GroupPredicate extends ChangeIndexPredicate {
@@ -25,7 +24,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     for (PatchSet ps : cd.patchSets()) {
       List<String> groups = ps.getGroups();
       if (groups != null && groups.contains(getValue())) {
diff --git a/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java b/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java
index e422b74..e57a8b3 100644
--- a/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java
+++ b/java/com/google/gerrit/server/query/change/HasDraftByPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class HasDraftByPredicate extends ChangeIndexPredicate {
   protected final Account.Id accountId;
@@ -27,7 +26,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     return cd.draftsByUser().contains(accountId);
   }
 
diff --git a/java/com/google/gerrit/server/query/change/HasStarsPredicate.java b/java/com/google/gerrit/server/query/change/HasStarsPredicate.java
index b17fffd..0c99cdf 100644
--- a/java/com/google/gerrit/server/query/change/HasStarsPredicate.java
+++ b/java/com/google/gerrit/server/query/change/HasStarsPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class HasStarsPredicate extends ChangeIndexPredicate {
   protected final Account.Id accountId;
@@ -27,7 +26,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     return cd.stars().containsKey(accountId);
   }
 
diff --git a/java/com/google/gerrit/server/query/change/HashtagPredicate.java b/java/com/google/gerrit/server/query/change/HashtagPredicate.java
index 95ecf89..1fe4af4 100644
--- a/java/com/google/gerrit/server/query/change/HashtagPredicate.java
+++ b/java/com/google/gerrit/server/query/change/HashtagPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.server.change.HashtagsUtil;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class HashtagPredicate extends ChangeIndexPredicate {
   public HashtagPredicate(String hashtag) {
@@ -26,7 +25,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     for (String hashtag : object.notes().load().getHashtags()) {
       if (hashtag.equalsIgnoreCase(getValue())) {
         return true;
diff --git a/java/com/google/gerrit/server/query/change/InternalChangeQuery.java b/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
index 6bd9025..74a0f71 100644
--- a/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
+++ b/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.index.change.ChangeIndexCollection;
 import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -91,19 +90,19 @@
     this.notesFactory = notesFactory;
   }
 
-  public List<ChangeData> byKey(Change.Key key) throws OrmException {
+  public List<ChangeData> byKey(Change.Key key) {
     return byKeyPrefix(key.get());
   }
 
-  public List<ChangeData> byKeyPrefix(String prefix) throws OrmException {
+  public List<ChangeData> byKeyPrefix(String prefix) {
     return query(new ChangeIdPredicate(prefix));
   }
 
-  public List<ChangeData> byLegacyChangeId(Change.Id id) throws OrmException {
+  public List<ChangeData> byLegacyChangeId(Change.Id id) {
     return query(new LegacyChangeIdPredicate(id));
   }
 
-  public List<ChangeData> byLegacyChangeIds(Collection<Change.Id> ids) throws OrmException {
+  public List<ChangeData> byLegacyChangeIds(Collection<Change.Id> ids) {
     List<Predicate<ChangeData>> preds = new ArrayList<>(ids.size());
     for (Change.Id id : ids) {
       preds.add(new LegacyChangeIdPredicate(id));
@@ -111,12 +110,11 @@
     return query(or(preds));
   }
 
-  public List<ChangeData> byBranchKey(Branch.NameKey branch, Change.Key key) throws OrmException {
+  public List<ChangeData> byBranchKey(Branch.NameKey branch, Change.Key key) {
     return query(byBranchKeyPred(branch, key));
   }
 
-  public List<ChangeData> byBranchKeyOpen(Project.NameKey project, String branch, Change.Key key)
-      throws OrmException {
+  public List<ChangeData> byBranchKeyOpen(Project.NameKey project, String branch, Change.Key key) {
     return query(and(byBranchKeyPred(new Branch.NameKey(project, branch), key), open()));
   }
 
@@ -129,21 +127,20 @@
     return and(ref(branch), project(branch.getParentKey()), change(key));
   }
 
-  public List<ChangeData> byProject(Project.NameKey project) throws OrmException {
+  public List<ChangeData> byProject(Project.NameKey project) {
     return query(project(project));
   }
 
-  public List<ChangeData> byBranchOpen(Branch.NameKey branch) throws OrmException {
+  public List<ChangeData> byBranchOpen(Branch.NameKey branch) {
     return query(and(ref(branch), project(branch.getParentKey()), open()));
   }
 
-  public List<ChangeData> byBranchNew(Branch.NameKey branch) throws OrmException {
+  public List<ChangeData> byBranchNew(Branch.NameKey branch) {
     return query(and(ref(branch), project(branch.getParentKey()), status(Change.Status.NEW)));
   }
 
   public Iterable<ChangeData> byCommitsOnBranchNotMerged(
-      Repository repo, Branch.NameKey branch, Collection<String> hashes)
-      throws OrmException, IOException {
+      Repository repo, Branch.NameKey branch, Collection<String> hashes) throws IOException {
     return byCommitsOnBranchNotMerged(
         repo,
         branch,
@@ -155,7 +152,7 @@
   @VisibleForTesting
   Iterable<ChangeData> byCommitsOnBranchNotMerged(
       Repository repo, Branch.NameKey branch, Collection<String> hashes, int indexLimit)
-      throws OrmException, IOException {
+      throws IOException {
     if (hashes.size() > indexLimit) {
       return byCommitsOnBranchNotMergedFromDatabase(repo, branch, hashes);
     }
@@ -163,8 +160,7 @@
   }
 
   private Iterable<ChangeData> byCommitsOnBranchNotMergedFromDatabase(
-      Repository repo, Branch.NameKey branch, Collection<String> hashes)
-      throws OrmException, IOException {
+      Repository repo, Branch.NameKey branch, Collection<String> hashes) throws IOException {
     Set<Change.Id> changeIds = Sets.newHashSetWithExpectedSize(hashes.size());
     String lastPrefix = null;
     for (Ref ref : repo.getRefDatabase().getRefsByPrefix(RefNames.REFS_CHANGES)) {
@@ -194,7 +190,7 @@
   }
 
   private Iterable<ChangeData> byCommitsOnBranchNotMergedFromIndex(
-      Branch.NameKey branch, Collection<String> hashes) throws OrmException {
+      Branch.NameKey branch, Collection<String> hashes) {
     return query(
         and(
             ref(branch),
@@ -211,50 +207,45 @@
     return commits;
   }
 
-  public List<ChangeData> byProjectOpen(Project.NameKey project) throws OrmException {
+  public List<ChangeData> byProjectOpen(Project.NameKey project) {
     return query(and(project(project), open()));
   }
 
-  public List<ChangeData> byTopicOpen(String topic) throws OrmException {
+  public List<ChangeData> byTopicOpen(String topic) {
     return query(and(new ExactTopicPredicate(topic), open()));
   }
 
-  public List<ChangeData> byCommit(ObjectId id) throws OrmException {
+  public List<ChangeData> byCommit(ObjectId id) {
     return byCommit(id.name());
   }
 
-  public List<ChangeData> byCommit(String hash) throws OrmException {
+  public List<ChangeData> byCommit(String hash) {
     return query(commit(hash));
   }
 
-  public List<ChangeData> byProjectCommit(Project.NameKey project, ObjectId id)
-      throws OrmException {
+  public List<ChangeData> byProjectCommit(Project.NameKey project, ObjectId id) {
     return byProjectCommit(project, id.name());
   }
 
-  public List<ChangeData> byProjectCommit(Project.NameKey project, String hash)
-      throws OrmException {
+  public List<ChangeData> byProjectCommit(Project.NameKey project, String hash) {
     return query(and(project(project), commit(hash)));
   }
 
-  public List<ChangeData> byProjectCommits(Project.NameKey project, List<String> hashes)
-      throws OrmException {
+  public List<ChangeData> byProjectCommits(Project.NameKey project, List<String> hashes) {
     int n = indexConfig.maxTerms() - 1;
     checkArgument(hashes.size() <= n, "cannot exceed %s commits", n);
     return query(and(project(project), or(commits(hashes))));
   }
 
-  public List<ChangeData> byBranchCommit(String project, String branch, String hash)
-      throws OrmException {
+  public List<ChangeData> byBranchCommit(String project, String branch, String hash) {
     return query(byBranchCommitPred(project, branch, hash));
   }
 
-  public List<ChangeData> byBranchCommit(Branch.NameKey branch, String hash) throws OrmException {
+  public List<ChangeData> byBranchCommit(Branch.NameKey branch, String hash) {
     return byBranchCommit(branch.getParentKey().get(), branch.get(), hash);
   }
 
-  public List<ChangeData> byBranchCommitOpen(String project, String branch, String hash)
-      throws OrmException {
+  public List<ChangeData> byBranchCommitOpen(String project, String branch, String hash) {
     return query(and(byBranchCommitPred(project, branch, hash), open()));
   }
 
@@ -268,7 +259,7 @@
     return and(new ProjectPredicate(project), new RefPredicate(branch), commit(hash));
   }
 
-  public List<ChangeData> bySubmissionId(String cs) throws OrmException {
+  public List<ChangeData> bySubmissionId(String cs) {
     if (Strings.isNullOrEmpty(cs)) {
       return Collections.emptyList();
     }
@@ -290,8 +281,7 @@
       Provider<InternalChangeQuery> queryProvider,
       IndexConfig indexConfig,
       Project.NameKey project,
-      Collection<String> groups)
-      throws OrmException {
+      Collection<String> groups) {
     // These queries may be complex along multiple dimensions:
     //  * Many groups per change, if there are very many patch sets. This requires partitioning the
     //    list of predicates and combining results.
diff --git a/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java b/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java
index 7ff5a28..ddb6f32 100644
--- a/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java
+++ b/java/com/google/gerrit/server/query/change/IsReviewedPredicate.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -48,7 +47,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     Set<Account.Id> reviewedBy = cd.reviewedBy();
     return !reviewedBy.isEmpty() ? reviewedBy.contains(id) : id.equals(NOT_REVIEWED);
   }
diff --git a/java/com/google/gerrit/server/query/change/IsUnresolvedPredicate.java b/java/com/google/gerrit/server/query/change/IsUnresolvedPredicate.java
index 225dc454..27309af 100644
--- a/java/com/google/gerrit/server/query/change/IsUnresolvedPredicate.java
+++ b/java/com/google/gerrit/server/query/change/IsUnresolvedPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class IsUnresolvedPredicate extends IntegerRangeChangePredicate {
   public IsUnresolvedPredicate() throws QueryParseException {
@@ -28,7 +27,7 @@
   }
 
   @Override
-  protected Integer getValueInt(ChangeData changeData) throws OrmException {
+  protected Integer getValueInt(ChangeData changeData) {
     return ChangeField.UNRESOLVED_COMMENT_COUNT.get(changeData);
   }
 }
diff --git a/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java b/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
index b698181..6028f2d 100644
--- a/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
+++ b/java/com/google/gerrit/server/query/change/IsWatchedByPredicate.java
@@ -81,7 +81,7 @@
       }
     }
     if (r.isEmpty()) {
-      return none();
+      return ImmutableList.of(ChangeIndexPredicate.none());
     } else if (checkIsVisible) {
       return ImmutableList.of(or(r), builder.is_visible());
     } else {
@@ -98,11 +98,6 @@
     return Collections.emptySet();
   }
 
-  protected static List<Predicate<ChangeData>> none() {
-    Predicate<ChangeData> any = any();
-    return ImmutableList.of(not(any));
-  }
-
   @Override
   public int getCost() {
     return 1;
diff --git a/java/com/google/gerrit/server/query/change/MessagePredicate.java b/java/com/google/gerrit/server/query/change/MessagePredicate.java
index 0cfcedb..0bd8c88 100644
--- a/java/com/google/gerrit/server/query/change/MessagePredicate.java
+++ b/java/com/google/gerrit/server/query/change/MessagePredicate.java
@@ -14,12 +14,12 @@
 
 package com.google.gerrit.server.query.change;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.index.change.ChangeIndex;
 import com.google.gerrit.server.index.change.IndexedChangeQuery;
-import com.google.gwtorm.server.OrmException;
 
 /** Predicate to match changes that contains specified text in commit messages body. */
 public class MessagePredicate extends ChangeIndexPredicate {
@@ -31,7 +31,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     try {
       Predicate<ChangeData> p = Predicate.and(new LegacyChangeIdPredicate(object.getId()), this);
       for (ChangeData cData : index.getSource(p, IndexedChangeQuery.oneResult()).read()) {
@@ -40,7 +40,7 @@
         }
       }
     } catch (QueryParseException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
 
     return false;
diff --git a/java/com/google/gerrit/server/query/change/OrSource.java b/java/com/google/gerrit/server/query/change/OrSource.java
index dcbf517..ba06b89 100644
--- a/java/com/google/gerrit/server/query/change/OrSource.java
+++ b/java/com/google/gerrit/server/query/change/OrSource.java
@@ -14,13 +14,13 @@
 
 package com.google.gerrit.server.query.change;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.query.FieldBundle;
 import com.google.gerrit.index.query.ListResultSet;
 import com.google.gerrit.index.query.OrPredicate;
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.index.query.ResultSet;
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwtorm.server.OrmException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
@@ -35,7 +35,7 @@
   }
 
   @Override
-  public ResultSet<ChangeData> read() throws OrmException {
+  public ResultSet<ChangeData> read() {
     // TODO(spearce) This probably should be more lazy.
     //
     List<ChangeData> r = new ArrayList<>();
@@ -48,14 +48,14 @@
           }
         }
       } else {
-        throw new OrmException("No ChangeDataSource: " + p);
+        throw new StorageException("No ChangeDataSource: " + p);
       }
     }
     return new ListResultSet<>(r);
   }
 
   @Override
-  public ResultSet<FieldBundle> readRaw() throws OrmException {
+  public ResultSet<FieldBundle> readRaw() {
     throw new UnsupportedOperationException("not implemented");
   }
 
diff --git a/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
index 09ff687..08e6f33 100644
--- a/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
+++ b/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
@@ -19,6 +19,7 @@
 
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.query.QueryParseException;
 import com.google.gerrit.index.query.QueryResult;
 import com.google.gerrit.reviewdb.client.PatchSet;
@@ -34,7 +35,6 @@
 import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.gson.Gson;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.BufferedWriter;
 import java.io.IOException;
@@ -219,7 +219,7 @@
         stats.moreChanges = results.more();
         stats.runTimeMilliseconds = TimeUtil.nowMs() - stats.runTimeMilliseconds;
         show(stats);
-      } catch (OrmException err) {
+      } catch (StorageException err) {
         logger.atSevere().withCause(err).log("Cannot execute query: %s", queryString);
 
         ErrorMessage m = new ErrorMessage();
@@ -242,7 +242,7 @@
 
   private ChangeAttribute buildChangeAttribute(
       ChangeData d, Map<Project.NameKey, Repository> repos, Map<Project.NameKey, RevWalk> revWalks)
-      throws OrmException, IOException {
+      throws IOException {
     LabelTypes labelTypes = d.getLabelTypes();
     ChangeAttribute c = eventFactory.asChangeAttribute(d.change(), d.notes());
     eventFactory.extend(c, d.change());
diff --git a/java/com/google/gerrit/server/query/change/OwnerPredicate.java b/java/com/google/gerrit/server/query/change/OwnerPredicate.java
index ff494fc..100a66c 100644
--- a/java/com/google/gerrit/server/query/change/OwnerPredicate.java
+++ b/java/com/google/gerrit/server/query/change/OwnerPredicate.java
@@ -17,7 +17,6 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class OwnerPredicate extends ChangeIndexPredicate {
   protected final Account.Id id;
@@ -32,7 +31,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     Change change = object.change();
     return change != null && id.equals(change.getOwner());
   }
diff --git a/java/com/google/gerrit/server/query/change/OwnerinPredicate.java b/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
index c48bdd5..41b3204 100644
--- a/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
+++ b/java/com/google/gerrit/server/query/change/OwnerinPredicate.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.IdentifiedUser;
-import com.google.gwtorm.server.OrmException;
 
 public class OwnerinPredicate extends PostFilterPredicate<ChangeData> {
   protected final IdentifiedUser.GenericFactory userFactory;
@@ -31,7 +30,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     final Change change = object.change();
     if (change == null) {
       return false;
diff --git a/java/com/google/gerrit/server/query/change/ProjectPredicate.java b/java/com/google/gerrit/server/query/change/ProjectPredicate.java
index 09a46a4..febd6c6 100644
--- a/java/com/google/gerrit/server/query/change/ProjectPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ProjectPredicate.java
@@ -17,7 +17,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class ProjectPredicate extends ChangeIndexPredicate {
   public ProjectPredicate(String id) {
@@ -29,7 +28,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     Change change = object.change();
     if (change == null) {
       return false;
diff --git a/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java b/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java
index 28b1302..744f4d2 100644
--- a/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ProjectPrefixPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class ProjectPrefixPredicate extends ChangeIndexPredicate {
   public ProjectPrefixPredicate(String prefix) {
@@ -24,7 +23,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     Change c = object.change();
     return c != null && c.getDest().getParentKey().get().startsWith(getValue());
   }
diff --git a/java/com/google/gerrit/server/query/change/RefPredicate.java b/java/com/google/gerrit/server/query/change/RefPredicate.java
index c9314e4..2ed4c99 100644
--- a/java/com/google/gerrit/server/query/change/RefPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RefPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class RefPredicate extends ChangeIndexPredicate {
   public RefPredicate(String ref) {
@@ -24,7 +23,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     Change change = object.change();
     if (change == null) {
       return false;
diff --git a/java/com/google/gerrit/server/query/change/RegexDirectoryPredicate.java b/java/com/google/gerrit/server/query/change/RegexDirectoryPredicate.java
index 1d49f1e..1787c76 100644
--- a/java/com/google/gerrit/server/query/change/RegexDirectoryPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RegexDirectoryPredicate.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.query.change;
 
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 import dk.brics.automaton.RegExp;
 import dk.brics.automaton.RunAutomaton;
 
@@ -37,7 +36,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     return ChangeField.getDirectories(cd).stream().anyMatch(pattern::run);
   }
 
diff --git a/java/com/google/gerrit/server/query/change/RegexPathPredicate.java b/java/com/google/gerrit/server/query/change/RegexPathPredicate.java
index f694904..4c3c04c 100644
--- a/java/com/google/gerrit/server/query/change/RegexPathPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RegexPathPredicate.java
@@ -16,9 +16,6 @@
 
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.ioutil.RegexListSearcher;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
-import java.util.List;
 
 public class RegexPathPredicate extends ChangeRegexPredicate {
   public RegexPathPredicate(String re) {
@@ -26,14 +23,11 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
-    List<String> files;
-    try {
-      files = object.currentFilePaths();
-    } catch (IOException e) {
-      throw new OrmException(e);
-    }
-    return RegexListSearcher.ofStrings(getValue()).search(files).findAny().isPresent();
+  public boolean match(ChangeData object) {
+    return RegexListSearcher.ofStrings(getValue())
+        .search(object.currentFilePaths())
+        .findAny()
+        .isPresent();
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java b/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java
index 1efc77d..46a17f6 100644
--- a/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RegexProjectPredicate.java
@@ -17,7 +17,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 import dk.brics.automaton.RegExp;
 import dk.brics.automaton.RunAutomaton;
 
@@ -39,7 +38,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     Change change = object.change();
     if (change == null) {
       return false;
diff --git a/java/com/google/gerrit/server/query/change/RegexRefPredicate.java b/java/com/google/gerrit/server/query/change/RegexRefPredicate.java
index 92abafb..af211e6 100644
--- a/java/com/google/gerrit/server/query/change/RegexRefPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RegexRefPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 import dk.brics.automaton.RegExp;
 import dk.brics.automaton.RunAutomaton;
 
@@ -38,7 +37,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     Change change = object.change();
     if (change == null) {
       return false;
diff --git a/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java b/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java
index 2b58c88..0441afa 100644
--- a/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RegexTopicPredicate.java
@@ -17,7 +17,6 @@
 import static com.google.gerrit.server.index.change.ChangeField.EXACT_TOPIC;
 
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwtorm.server.OrmException;
 import dk.brics.automaton.RegExp;
 import dk.brics.automaton.RunAutomaton;
 
@@ -39,7 +38,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     Change change = object.change();
     if (change == null || change.getTopic() == null) {
       return false;
diff --git a/java/com/google/gerrit/server/query/change/RevertOfPredicate.java b/java/com/google/gerrit/server/query/change/RevertOfPredicate.java
index 7f4ade0..eea1b1e 100644
--- a/java/com/google/gerrit/server/query/change/RevertOfPredicate.java
+++ b/java/com/google/gerrit/server/query/change/RevertOfPredicate.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.query.change;
 
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class RevertOfPredicate extends ChangeIndexPredicate {
   public RevertOfPredicate(String revertOf) {
@@ -23,7 +22,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     if (cd.change().getRevertOf() == null) {
       return false;
     }
diff --git a/java/com/google/gerrit/server/query/change/ReviewerByEmailPredicate.java b/java/com/google/gerrit/server/query/change/ReviewerByEmailPredicate.java
index 667c630..070f800 100644
--- a/java/com/google/gerrit/server/query/change/ReviewerByEmailPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ReviewerByEmailPredicate.java
@@ -20,7 +20,6 @@
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
-import com.google.gwtorm.server.OrmException;
 
 class ReviewerByEmailPredicate extends ChangeIndexPredicate {
 
@@ -43,7 +42,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     return cd.reviewersByEmail().asTable().get(state, adr) != null;
   }
 
diff --git a/java/com/google/gerrit/server/query/change/ReviewerPredicate.java b/java/com/google/gerrit/server/query/change/ReviewerPredicate.java
index 0d1ae44..19104d3 100644
--- a/java/com/google/gerrit/server/query/change/ReviewerPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ReviewerPredicate.java
@@ -20,7 +20,6 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.index.change.ChangeField;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
-import com.google.gwtorm.server.OrmException;
 
 public class ReviewerPredicate extends ChangeIndexPredicate {
   protected static Predicate<ChangeData> forState(Account.Id id, ReviewerStateInternal state) {
@@ -50,7 +49,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     return cd.reviewers().asTable().get(state, id) != null;
   }
 
diff --git a/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java b/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
index a0aa8b5..542a357 100644
--- a/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
+++ b/java/com/google/gerrit/server/query/change/ReviewerinPredicate.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
-import com.google.gwtorm.server.OrmException;
 
 public class ReviewerinPredicate extends PostFilterPredicate<ChangeData> {
   protected final IdentifiedUser.GenericFactory userFactory;
@@ -36,7 +35,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     for (Account.Id accountId : object.reviewers().byState(ReviewerStateInternal.REVIEWER)) {
       IdentifiedUser reviewer = userFactory.create(accountId);
       if (reviewer.getEffectiveGroups().contains(uuid)) {
diff --git a/java/com/google/gerrit/server/query/change/StarPredicate.java b/java/com/google/gerrit/server/query/change/StarPredicate.java
index 12d4753..6c5fd78 100644
--- a/java/com/google/gerrit/server/query/change/StarPredicate.java
+++ b/java/com/google/gerrit/server/query/change/StarPredicate.java
@@ -17,7 +17,6 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class StarPredicate extends ChangeIndexPredicate {
   protected final Account.Id accountId;
@@ -30,7 +29,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     return cd.stars().get(accountId).contains(label);
   }
 
diff --git a/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java b/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java
index 5fdeb68..0995a59 100644
--- a/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java
+++ b/java/com/google/gerrit/server/query/change/SubmissionIdPredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class SubmissionIdPredicate extends ChangeIndexPredicate {
   public SubmissionIdPredicate(String changeSet) {
@@ -24,7 +23,7 @@
   }
 
   @Override
-  public boolean match(ChangeData object) throws OrmException {
+  public boolean match(ChangeData object) {
     Change change = object.change();
     if (change == null) {
       return false;
diff --git a/java/com/google/gerrit/server/query/change/SubmitRecordPredicate.java b/java/com/google/gerrit/server/query/change/SubmitRecordPredicate.java
index 6a81ff6..e59ae43 100644
--- a/java/com/google/gerrit/server/query/change/SubmitRecordPredicate.java
+++ b/java/com/google/gerrit/server/query/change/SubmitRecordPredicate.java
@@ -20,7 +20,6 @@
 import com.google.gerrit.index.query.Predicate;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 import java.util.Set;
 
 public class SubmitRecordPredicate extends ChangeIndexPredicate {
@@ -41,7 +40,7 @@
   }
 
   @Override
-  public boolean match(ChangeData in) throws OrmException {
+  public boolean match(ChangeData in) {
     return ChangeField.formatSubmitRecordValues(in).contains(getValue());
   }
 
diff --git a/java/com/google/gerrit/server/query/change/SubmittablePredicate.java b/java/com/google/gerrit/server/query/change/SubmittablePredicate.java
index 8a2c889..c507f1c 100644
--- a/java/com/google/gerrit/server/query/change/SubmittablePredicate.java
+++ b/java/com/google/gerrit/server/query/change/SubmittablePredicate.java
@@ -16,7 +16,6 @@
 
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
 
 public class SubmittablePredicate extends ChangeIndexPredicate {
   protected final SubmitRecord.Status status;
@@ -27,7 +26,7 @@
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
+  public boolean match(ChangeData cd) {
     return cd.submitRecords(ChangeField.SUBMIT_RULE_OPTIONS_STRICT).stream()
         .anyMatch(r -> r.status == status);
   }
diff --git a/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java b/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java
index 4f751c5..622fa2c 100644
--- a/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java
+++ b/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java
@@ -14,26 +14,16 @@
 
 package com.google.gerrit.server.query.change;
 
-import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.server.index.change.ChangeField;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
 
 public class TrackingIdPredicate extends ChangeIndexPredicate {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
   public TrackingIdPredicate(String trackingId) {
     super(ChangeField.TR, trackingId);
   }
 
   @Override
-  public boolean match(ChangeData cd) throws OrmException {
-    try {
-      return cd.trackingFooters().containsValue(getValue());
-    } catch (IOException e) {
-      logger.atWarning().withCause(e).log("Cannot extract footers from %s", cd.getId());
-    }
-    return false;
+  public boolean match(ChangeData cd) {
+    return cd.trackingFooters().containsValue(getValue());
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/query/group/GroupIsVisibleToPredicate.java b/java/com/google/gerrit/server/query/group/GroupIsVisibleToPredicate.java
index 144a81c..8248bf5 100644
--- a/java/com/google/gerrit/server/query/group/GroupIsVisibleToPredicate.java
+++ b/java/com/google/gerrit/server/query/group/GroupIsVisibleToPredicate.java
@@ -15,14 +15,13 @@
 package com.google.gerrit.server.query.group;
 
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.index.query.IsVisibleToPredicate;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.group.InternalGroup;
 import com.google.gerrit.server.index.IndexUtils;
 import com.google.gerrit.server.query.account.AccountQueryBuilder;
-import com.google.gwtorm.server.OrmException;
 
 public class GroupIsVisibleToPredicate extends IsVisibleToPredicate<InternalGroup> {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -38,7 +37,7 @@
   }
 
   @Override
-  public boolean match(InternalGroup group) throws OrmException {
+  public boolean match(InternalGroup group) {
     try {
       boolean canSee = groupControlFactory.controlFor(user, group.getGroupUUID()).isVisible();
       if (!canSee) {
diff --git a/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java b/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java
index d5dc692..215e36f 100644
--- a/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/group/GroupQueryBuilder.java
@@ -33,7 +33,6 @@
 import com.google.gerrit.server.account.GroupBackends;
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.group.InternalGroup;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.List;
@@ -42,7 +41,7 @@
 import org.eclipse.jgit.errors.ConfigInvalidException;
 
 /** Parses a query string meant to be applied to group objects. */
-public class GroupQueryBuilder extends QueryBuilder<InternalGroup> {
+public class GroupQueryBuilder extends QueryBuilder<InternalGroup, GroupQueryBuilder> {
   public static final String FIELD_UUID = "uuid";
   public static final String FIELD_DESCRIPTION = "description";
   public static final String FIELD_INNAME = "inname";
@@ -70,7 +69,7 @@
 
   @Inject
   GroupQueryBuilder(Arguments args) {
-    super(mydef);
+    super(mydef, null);
     this.args = args;
   }
 
@@ -135,7 +134,7 @@
 
   @Operator
   public Predicate<InternalGroup> member(String query)
-      throws QueryParseException, OrmException, ConfigInvalidException, IOException {
+      throws QueryParseException, ConfigInvalidException, IOException {
     Set<Account.Id> accounts = parseAccount(query);
     List<Predicate<InternalGroup>> predicates =
         accounts.stream().map(GroupPredicates::member).collect(toImmutableList());
@@ -158,7 +157,7 @@
   }
 
   private Set<Account.Id> parseAccount(String nameOrEmail)
-      throws QueryParseException, OrmException, IOException, ConfigInvalidException {
+      throws QueryParseException, IOException, ConfigInvalidException {
     try {
       return args.accountResolver.resolve(nameOrEmail).asNonEmptyIdSet();
     } catch (UnresolvableAccountException e) {
diff --git a/java/com/google/gerrit/server/query/group/InternalGroupQuery.java b/java/com/google/gerrit/server/query/group/InternalGroupQuery.java
index 42b38bf..5749809 100644
--- a/java/com/google/gerrit/server/query/group/InternalGroupQuery.java
+++ b/java/com/google/gerrit/server/query/group/InternalGroupQuery.java
@@ -26,7 +26,6 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.group.InternalGroup;
 import com.google.gerrit.server.index.group.GroupIndexCollection;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.util.List;
 import java.util.Optional;
@@ -46,24 +45,24 @@
     super(queryProcessor, indexes, indexConfig);
   }
 
-  public Optional<InternalGroup> byName(AccountGroup.NameKey groupName) throws OrmException {
+  public Optional<InternalGroup> byName(AccountGroup.NameKey groupName) {
     return getOnlyGroup(GroupPredicates.name(groupName.get()), "group name '" + groupName + "'");
   }
 
-  public Optional<InternalGroup> byId(AccountGroup.Id groupId) throws OrmException {
+  public Optional<InternalGroup> byId(AccountGroup.Id groupId) {
     return getOnlyGroup(GroupPredicates.id(groupId), "group id '" + groupId + "'");
   }
 
-  public List<InternalGroup> byMember(Account.Id memberId) throws OrmException {
+  public List<InternalGroup> byMember(Account.Id memberId) {
     return query(GroupPredicates.member(memberId));
   }
 
-  public List<InternalGroup> bySubgroup(AccountGroup.UUID subgroupId) throws OrmException {
+  public List<InternalGroup> bySubgroup(AccountGroup.UUID subgroupId) {
     return query(GroupPredicates.subgroup(subgroupId));
   }
 
   private Optional<InternalGroup> getOnlyGroup(
-      Predicate<InternalGroup> predicate, String groupDescription) throws OrmException {
+      Predicate<InternalGroup> predicate, String groupDescription) {
     List<InternalGroup> groups = query(predicate);
     if (groups.isEmpty()) {
       return Optional.empty();
diff --git a/java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java b/java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java
index b1c5af0..2c84b9a 100644
--- a/java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java
+++ b/java/com/google/gerrit/server/query/project/ProjectIsVisibleToPredicate.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.query.account.AccountQueryBuilder;
-import com.google.gwtorm.server.OrmException;
 
 public class ProjectIsVisibleToPredicate extends IsVisibleToPredicate<ProjectData> {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -37,7 +36,7 @@
   }
 
   @Override
-  public boolean match(ProjectData pd) throws OrmException {
+  public boolean match(ProjectData pd) {
     if (!pd.getProject().getState().permitsRead()) {
       logger.atFine().log("Filter out non-readable project: %s", pd);
       return false;
diff --git a/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java b/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
index 4923015..570da6b 100644
--- a/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/project/ProjectQueryBuilder.java
@@ -28,7 +28,7 @@
 import java.util.List;
 
 /** Parses a query string meant to be applied to project objects. */
-public class ProjectQueryBuilder extends QueryBuilder<ProjectData> {
+public class ProjectQueryBuilder extends QueryBuilder<ProjectData, ProjectQueryBuilder> {
   public static final String FIELD_LIMIT = "limit";
 
   private static final QueryBuilder.Definition<ProjectData, ProjectQueryBuilder> mydef =
@@ -36,7 +36,7 @@
 
   @Inject
   ProjectQueryBuilder() {
-    super(mydef);
+    super(mydef, null);
   }
 
   @Operator
diff --git a/java/com/google/gerrit/server/restapi/BUILD b/java/com/google/gerrit/server/restapi/BUILD
index 0477a63..3040644 100644
--- a/java/com/google/gerrit/server/restapi/BUILD
+++ b/java/com/google/gerrit/server/restapi/BUILD
@@ -8,6 +8,7 @@
     deps = [
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/index",
         "//java/com/google/gerrit/index:query_exception",
@@ -27,7 +28,6 @@
         "//lib:blame-cache",
         "//lib:gson",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:servlet-api-3_1",
         "//lib/auto:auto-value",
         "//lib/auto:auto-value-annotations",
diff --git a/java/com/google/gerrit/server/restapi/access/ListAccess.java b/java/com/google/gerrit/server/restapi/access/ListAccess.java
index 3f01c6c..a3e9530 100644
--- a/java/com/google/gerrit/server/restapi/access/ListAccess.java
+++ b/java/com/google/gerrit/server/restapi/access/ListAccess.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.restapi.project.GetAccess;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -50,7 +49,7 @@
   @Override
   public Map<String, ProjectAccessInfo> apply(TopLevelResource resource)
       throws ResourceNotFoundException, ResourceConflictException, IOException,
-          PermissionBackendException, OrmException {
+          PermissionBackendException {
     Map<String, ProjectAccessInfo> access = new TreeMap<>();
     for (String p : projects) {
       access.put(p, getAccess.apply(new Project.NameKey(p)));
diff --git a/java/com/google/gerrit/server/restapi/account/AccountsCollection.java b/java/com/google/gerrit/server/restapi/account/AccountsCollection.java
index 35922f4..119e2e4 100644
--- a/java/com/google/gerrit/server/restapi/account/AccountsCollection.java
+++ b/java/com/google/gerrit/server/restapi/account/AccountsCollection.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.server.account.AccountResolver;
 import com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException;
 import com.google.gerrit.server.account.AccountResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -49,8 +48,7 @@
 
   @Override
   public AccountResource parse(TopLevelResource root, IdString id)
-      throws ResourceNotFoundException, AuthException, OrmException, IOException,
-          ConfigInvalidException {
+      throws ResourceNotFoundException, AuthException, IOException, ConfigInvalidException {
     try {
       return new AccountResource(accountResolver.resolve(id.get()).asUniqueUser());
     } catch (UnresolvableAccountException e) {
diff --git a/java/com/google/gerrit/server/restapi/account/AddSshKey.java b/java/com/google/gerrit/server/restapi/account/AddSshKey.java
index e47540d..7c05f4e 100644
--- a/java/com/google/gerrit/server/restapi/account/AddSshKey.java
+++ b/java/com/google/gerrit/server/restapi/account/AddSshKey.java
@@ -18,8 +18,8 @@
 
 import com.google.common.flogger.FluentLogger;
 import com.google.common.io.ByteSource;
-import com.google.gerrit.common.errors.EmailException;
-import com.google.gerrit.common.errors.InvalidSshKeyException;
+import com.google.gerrit.exceptions.EmailException;
+import com.google.gerrit.exceptions.InvalidSshKeyException;
 import com.google.gerrit.extensions.api.accounts.SshKeyInput;
 import com.google.gerrit.extensions.common.SshKeyInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -37,7 +37,6 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.ssh.SshKeyCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -72,7 +71,7 @@
 
   @Override
   public Response<SshKeyInfo> apply(AccountResource rsrc, SshKeyInput input)
-      throws AuthException, BadRequestException, OrmException, IOException, ConfigInvalidException,
+      throws AuthException, BadRequestException, IOException, ConfigInvalidException,
           PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
diff --git a/java/com/google/gerrit/server/restapi/account/CreateAccount.java b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
index b704fd1..b19994c 100644
--- a/java/com/google/gerrit/server/restapi/account/CreateAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
@@ -22,8 +22,8 @@
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.InvalidSshKeyException;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.InvalidSshKeyException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.api.accounts.AccountInput;
 import com.google.gerrit.extensions.common.AccountInfo;
@@ -52,7 +52,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.plugincontext.PluginSetContext;
 import com.google.gerrit.server.ssh.SshKeyCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -103,13 +102,13 @@
   public Response<AccountInfo> apply(
       TopLevelResource rsrc, IdString id, @Nullable AccountInput input)
       throws BadRequestException, ResourceConflictException, UnprocessableEntityException,
-          OrmException, IOException, ConfigInvalidException, PermissionBackendException {
+          IOException, ConfigInvalidException, PermissionBackendException {
     return apply(id, input != null ? input : new AccountInput());
   }
 
   public Response<AccountInfo> apply(IdString id, AccountInput input)
       throws BadRequestException, ResourceConflictException, UnprocessableEntityException,
-          OrmException, IOException, ConfigInvalidException, PermissionBackendException {
+          IOException, ConfigInvalidException, PermissionBackendException {
     String username = id.get();
     if (input.username != null && !username.equals(input.username)) {
       throw new BadRequestException("username must match URL");
@@ -189,7 +188,7 @@
   }
 
   private void addGroupMember(AccountGroup.UUID groupUuid, Account.Id accountId)
-      throws OrmException, IOException, NoSuchGroupException, ConfigInvalidException {
+      throws IOException, NoSuchGroupException, ConfigInvalidException {
     InternalGroupUpdate groupUpdate =
         InternalGroupUpdate.builder()
             .setMemberModification(memberIds -> Sets.union(memberIds, ImmutableSet.of(accountId)))
diff --git a/java/com/google/gerrit/server/restapi/account/CreateEmail.java b/java/com/google/gerrit/server/restapi/account/CreateEmail.java
index e4e8525..cc4cf21 100644
--- a/java/com/google/gerrit/server/restapi/account/CreateEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateEmail.java
@@ -17,7 +17,7 @@
 import static com.google.gerrit.extensions.client.AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT;
 
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.accounts.EmailInput;
 import com.google.gerrit.extensions.client.AccountFieldName;
 import com.google.gerrit.extensions.common.EmailInfo;
@@ -41,7 +41,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -84,7 +83,7 @@
 
   @Override
   public Response<EmailInfo> apply(AccountResource rsrc, IdString id, EmailInput input)
-      throws RestApiException, OrmException, EmailException, MethodNotAllowedException, IOException,
+      throws RestApiException, EmailException, MethodNotAllowedException, IOException,
           ConfigInvalidException, PermissionBackendException {
     if (input == null) {
       input = new EmailInput();
@@ -103,7 +102,7 @@
 
   /** To be used from plugins that want to create emails without permission checks. */
   public Response<EmailInfo> apply(IdentifiedUser user, IdString id, EmailInput input)
-      throws RestApiException, OrmException, EmailException, MethodNotAllowedException, IOException,
+      throws RestApiException, EmailException, MethodNotAllowedException, IOException,
           ConfigInvalidException, PermissionBackendException {
     String email = id.get().trim();
 
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteActive.java b/java/com/google/gerrit/server/restapi/account/DeleteActive.java
index 4302513..ffd7893 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteActive.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteActive.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.SetInactiveFlag;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -46,7 +45,7 @@
 
   @Override
   public Response<?> apply(AccountResource rsrc, Input input)
-      throws RestApiException, OrmException, IOException, ConfigInvalidException {
+      throws RestApiException, IOException, ConfigInvalidException {
     if (self.get().hasSameAccountId(rsrc.getUser())) {
       throw new ResourceConflictException("cannot deactivate own account");
     }
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java b/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
index 135718b..4788301 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteDraftComments.java
@@ -56,7 +56,6 @@
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -110,7 +109,7 @@
   @Override
   public ImmutableList<DeletedDraftCommentInfo> apply(
       AccountResource rsrc, DeleteDraftCommentsInput input)
-      throws RestApiException, OrmException, UpdateException {
+      throws RestApiException, UpdateException {
     CurrentUser user = userProvider.get();
     if (!user.isIdentifiedUser()) {
       throw new AuthException("Authentication required");
@@ -176,7 +175,7 @@
 
     @Override
     public boolean updateChange(ChangeContext ctx)
-        throws OrmException, PatchListNotAvailableException, PermissionBackendException {
+        throws PatchListNotAvailableException, PermissionBackendException {
       ImmutableList.Builder<CommentInfo> comments = ImmutableList.builder();
       boolean dirty = false;
       for (Comment c : commentsUtil.draftByChangeAuthor(ctx.getNotes(), accountId)) {
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteEmail.java b/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
index 6bacde2..7a03005 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
@@ -35,7 +35,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -69,7 +68,7 @@
   @Override
   public Response<?> apply(AccountResource.Email rsrc, Input input)
       throws AuthException, ResourceNotFoundException, ResourceConflictException,
-          MethodNotAllowedException, OrmException, IOException, ConfigInvalidException,
+          MethodNotAllowedException, IOException, ConfigInvalidException,
           PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
@@ -79,7 +78,7 @@
 
   public Response<?> apply(IdentifiedUser user, String email)
       throws ResourceNotFoundException, ResourceConflictException, MethodNotAllowedException,
-          OrmException, IOException, ConfigInvalidException {
+          IOException, ConfigInvalidException {
     if (!realm.allowsEdit(AccountFieldName.REGISTER_NEW_EMAIL)) {
       throw new MethodNotAllowedException("realm does not allow deleting emails");
     }
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java b/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
index 9fb535c..82b445f 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
@@ -33,7 +33,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -66,8 +65,7 @@
 
   @Override
   public Response<?> apply(AccountResource resource, List<String> extIds)
-      throws RestApiException, IOException, OrmException, ConfigInvalidException,
-          PermissionBackendException {
+      throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     if (!self.get().hasSameAccountId(resource.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
     }
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java b/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
index b7b3c83..787e083 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
@@ -25,7 +25,6 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.ssh.SshKeyCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -55,8 +54,8 @@
 
   @Override
   public Response<?> apply(AccountResource.SshKey rsrc, Input input)
-      throws AuthException, OrmException, RepositoryNotFoundException, IOException,
-          ConfigInvalidException, PermissionBackendException {
+      throws AuthException, RepositoryNotFoundException, IOException, ConfigInvalidException,
+          PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
     }
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
index deb70fe..798aad1 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
@@ -31,7 +31,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -59,8 +58,8 @@
 
   @Override
   public Response<?> apply(AccountResource rsrc, List<ProjectWatchInfo> input)
-      throws AuthException, UnprocessableEntityException, OrmException, IOException,
-          ConfigInvalidException, PermissionBackendException {
+      throws AuthException, UnprocessableEntityException, IOException, ConfigInvalidException,
+          PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
     }
diff --git a/java/com/google/gerrit/server/restapi/account/GetAccount.java b/java/com/google/gerrit/server/restapi/account/GetAccount.java
index 6b73ae3b..6544f8d 100644
--- a/java/com/google/gerrit/server/restapi/account/GetAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/GetAccount.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -33,7 +32,7 @@
   }
 
   @Override
-  public AccountInfo apply(AccountResource rsrc) throws OrmException, PermissionBackendException {
+  public AccountInfo apply(AccountResource rsrc) throws PermissionBackendException {
     AccountLoader loader = infoFactory.create(true);
     AccountInfo info = loader.get(rsrc.getUser().getAccountId());
     loader.fill();
diff --git a/java/com/google/gerrit/server/restapi/account/GetDetail.java b/java/com/google/gerrit/server/restapi/account/GetDetail.java
index 97d0c60..72044c4 100644
--- a/java/com/google/gerrit/server/restapi/account/GetDetail.java
+++ b/java/com/google/gerrit/server/restapi/account/GetDetail.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.InternalAccountDirectory;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.Collections;
@@ -37,8 +36,7 @@
   }
 
   @Override
-  public AccountDetailInfo apply(AccountResource rsrc)
-      throws OrmException, PermissionBackendException {
+  public AccountDetailInfo apply(AccountResource rsrc) throws PermissionBackendException {
     Account a = rsrc.getUser().getAccount();
     AccountDetailInfo info = new AccountDetailInfo(a.getId().get());
     info.registeredOn = a.getRegisteredOn();
diff --git a/java/com/google/gerrit/server/restapi/account/GetExternalIds.java b/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
index 7a420ab..ef448dc 100644
--- a/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
+++ b/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -60,7 +59,7 @@
 
   @Override
   public List<AccountExternalIdInfo> apply(AccountResource resource)
-      throws RestApiException, IOException, OrmException, PermissionBackendException {
+      throws RestApiException, IOException, PermissionBackendException {
     if (!self.get().hasSameAccountId(resource.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
     }
diff --git a/java/com/google/gerrit/server/restapi/account/GetGroups.java b/java/com/google/gerrit/server/restapi/account/GetGroups.java
index ad9746e..569ff76 100644
--- a/java/com/google/gerrit/server/restapi/account/GetGroups.java
+++ b/java/com/google/gerrit/server/restapi/account/GetGroups.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.restapi.account;
 
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.Account;
@@ -24,7 +24,6 @@
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.restapi.group.GroupJson;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.ArrayList;
@@ -43,8 +42,7 @@
   }
 
   @Override
-  public List<GroupInfo> apply(AccountResource resource)
-      throws OrmException, PermissionBackendException {
+  public List<GroupInfo> apply(AccountResource resource) throws PermissionBackendException {
     IdentifiedUser user = resource.getUser();
     Account.Id userId = user.getAccountId();
     Set<AccountGroup.UUID> knownGroups = user.getEffectiveGroups().getKnownGroups();
diff --git a/java/com/google/gerrit/server/restapi/account/GetSshKeys.java b/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
index a49f9df..408aa5f 100644
--- a/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
+++ b/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
@@ -27,7 +27,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -55,8 +54,8 @@
 
   @Override
   public List<SshKeyInfo> apply(AccountResource rsrc)
-      throws AuthException, OrmException, RepositoryNotFoundException, IOException,
-          ConfigInvalidException, PermissionBackendException {
+      throws AuthException, RepositoryNotFoundException, IOException, ConfigInvalidException,
+          PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
     }
diff --git a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
index ba8c72a..fce324e 100644
--- a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
@@ -33,7 +33,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -57,8 +56,8 @@
 
   @Override
   public List<ProjectWatchInfo> apply(AccountResource rsrc)
-      throws OrmException, AuthException, IOException, ConfigInvalidException,
-          PermissionBackendException, ResourceNotFoundException {
+      throws AuthException, IOException, ConfigInvalidException, PermissionBackendException,
+          ResourceNotFoundException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
     }
diff --git a/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
index f29a0eb..14bd492 100644
--- a/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.restapi.project.ProjectsCollection;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -66,8 +65,7 @@
 
   @Override
   public List<ProjectWatchInfo> apply(AccountResource rsrc, List<ProjectWatchInfo> input)
-      throws OrmException, RestApiException, IOException, ConfigInvalidException,
-          PermissionBackendException {
+      throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
     }
diff --git a/java/com/google/gerrit/server/restapi/account/PutActive.java b/java/com/google/gerrit/server/restapi/account/PutActive.java
index 8255781..a6ffaa6 100644
--- a/java/com/google/gerrit/server/restapi/account/PutActive.java
+++ b/java/com/google/gerrit/server/restapi/account/PutActive.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.account.AccountResource;
 import com.google.gerrit.server.account.SetInactiveFlag;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -41,7 +40,7 @@
 
   @Override
   public Response<String> apply(AccountResource rsrc, Input input)
-      throws RestApiException, OrmException, IOException, ConfigInvalidException {
+      throws RestApiException, IOException, ConfigInvalidException {
     return setInactiveFlag.activate(rsrc.getUser().getAccountId());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/account/PutAgreement.java b/java/com/google/gerrit/server/restapi/account/PutAgreement.java
index 0d8a816..7bac359 100644
--- a/java/com/google/gerrit/server/restapi/account/PutAgreement.java
+++ b/java/com/google/gerrit/server/restapi/account/PutAgreement.java
@@ -17,7 +17,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.data.ContributorAgreement;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.api.accounts.AgreementInput;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -35,7 +35,6 @@
 import com.google.gerrit.server.extensions.events.AgreementSignup;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.restapi.group.AddMembers;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -67,7 +66,7 @@
 
   @Override
   public Response<String> apply(AccountResource resource, AgreementInput input)
-      throws IOException, OrmException, RestApiException, ConfigInvalidException {
+      throws IOException, RestApiException, ConfigInvalidException {
     if (!agreementsEnabled) {
       throw new MethodNotAllowedException("contributor agreements disabled");
     }
diff --git a/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java b/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
index c6e2cb0..4b223ed 100644
--- a/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
+++ b/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -75,8 +74,8 @@
 
   @Override
   public Response<String> apply(AccountResource rsrc, HttpPasswordInput input)
-      throws AuthException, ResourceNotFoundException, ResourceConflictException, OrmException,
-          IOException, ConfigInvalidException, PermissionBackendException {
+      throws AuthException, ResourceNotFoundException, ResourceConflictException, IOException,
+          ConfigInvalidException, PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
     }
@@ -100,7 +99,7 @@
   }
 
   public Response<String> apply(IdentifiedUser user, String newPassword)
-      throws ResourceNotFoundException, ResourceConflictException, OrmException, IOException,
+      throws ResourceNotFoundException, ResourceConflictException, IOException,
           ConfigInvalidException {
     String userName =
         user.getUserName().orElseThrow(() -> new ResourceConflictException("username must be set"));
diff --git a/java/com/google/gerrit/server/restapi/account/PutName.java b/java/com/google/gerrit/server/restapi/account/PutName.java
index 1e00aac..30dfc66 100644
--- a/java/com/google/gerrit/server/restapi/account/PutName.java
+++ b/java/com/google/gerrit/server/restapi/account/PutName.java
@@ -32,7 +32,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -60,8 +59,8 @@
 
   @Override
   public Response<String> apply(AccountResource rsrc, NameInput input)
-      throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
-          IOException, PermissionBackendException, ConfigInvalidException {
+      throws AuthException, MethodNotAllowedException, ResourceNotFoundException, IOException,
+          PermissionBackendException, ConfigInvalidException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
     }
@@ -70,7 +69,7 @@
 
   public Response<String> apply(IdentifiedUser user, NameInput input)
       throws MethodNotAllowedException, ResourceNotFoundException, IOException,
-          ConfigInvalidException, OrmException {
+          ConfigInvalidException {
     if (input == null) {
       input = new NameInput();
     }
diff --git a/java/com/google/gerrit/server/restapi/account/PutPreferred.java b/java/com/google/gerrit/server/restapi/account/PutPreferred.java
index 265bf53..b8edec3 100644
--- a/java/com/google/gerrit/server/restapi/account/PutPreferred.java
+++ b/java/com/google/gerrit/server/restapi/account/PutPreferred.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -69,8 +68,7 @@
 
   @Override
   public Response<String> apply(AccountResource.Email rsrc, Input input)
-      throws RestApiException, OrmException, IOException, PermissionBackendException,
-          ConfigInvalidException {
+      throws RestApiException, IOException, PermissionBackendException, ConfigInvalidException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
     }
@@ -78,7 +76,7 @@
   }
 
   public Response<String> apply(IdentifiedUser user, String preferredEmail)
-      throws RestApiException, IOException, ConfigInvalidException, OrmException {
+      throws RestApiException, IOException, ConfigInvalidException {
     AtomicReference<Optional<RestApiException>> exception = new AtomicReference<>(Optional.empty());
     AtomicBoolean alreadyPreferred = new AtomicBoolean(false);
     accountsUpdateProvider
diff --git a/java/com/google/gerrit/server/restapi/account/PutStatus.java b/java/com/google/gerrit/server/restapi/account/PutStatus.java
index 9aee0a3..4f1128d 100644
--- a/java/com/google/gerrit/server/restapi/account/PutStatus.java
+++ b/java/com/google/gerrit/server/restapi/account/PutStatus.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -54,8 +53,8 @@
 
   @Override
   public Response<String> apply(AccountResource rsrc, StatusInput input)
-      throws AuthException, ResourceNotFoundException, OrmException, IOException,
-          PermissionBackendException, ConfigInvalidException {
+      throws AuthException, ResourceNotFoundException, IOException, PermissionBackendException,
+          ConfigInvalidException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
     }
@@ -63,7 +62,7 @@
   }
 
   public Response<String> apply(IdentifiedUser user, StatusInput input)
-      throws ResourceNotFoundException, IOException, ConfigInvalidException, OrmException {
+      throws ResourceNotFoundException, IOException, ConfigInvalidException {
     if (input == null) {
       input = new StatusInput();
     }
diff --git a/java/com/google/gerrit/server/restapi/account/PutUsername.java b/java/com/google/gerrit/server/restapi/account/PutUsername.java
index 856a5db..bc1ffc8 100644
--- a/java/com/google/gerrit/server/restapi/account/PutUsername.java
+++ b/java/com/google/gerrit/server/restapi/account/PutUsername.java
@@ -17,6 +17,7 @@
 import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_USERNAME;
 
 import com.google.common.base.Strings;
+import com.google.gerrit.exceptions.DuplicateKeyException;
 import com.google.gerrit.extensions.api.accounts.UsernameInput;
 import com.google.gerrit.extensions.client.AccountFieldName;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -36,8 +37,6 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.ssh.SshKeyCache;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -73,7 +72,7 @@
   @Override
   public String apply(AccountResource rsrc, UsernameInput input)
       throws AuthException, MethodNotAllowedException, UnprocessableEntityException,
-          ResourceConflictException, OrmException, IOException, ConfigInvalidException,
+          ResourceConflictException, IOException, ConfigInvalidException,
           PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
@@ -108,7 +107,7 @@
               "Set Username via API",
               accountId,
               u -> u.addExternalId(ExternalId.create(key, accountId, null, null)));
-    } catch (OrmDuplicateKeyException dupeErr) {
+    } catch (DuplicateKeyException dupeErr) {
       // If we are using this identity, don't report the exception.
       Optional<ExternalId> other = externalIds.get(key);
       if (other.isPresent() && other.get().accountId().equals(accountId)) {
diff --git a/java/com/google/gerrit/server/restapi/account/QueryAccounts.java b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
index f527ba4..e10d8bf 100644
--- a/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
+++ b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
@@ -41,7 +41,6 @@
 import com.google.gerrit.server.query.account.AccountPredicates;
 import com.google.gerrit.server.query.account.AccountQueryBuilder;
 import com.google.gerrit.server.query.account.AccountQueryProcessor;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.util.Collections;
 import java.util.EnumSet;
@@ -149,7 +148,7 @@
 
   @Override
   public List<AccountInfo> apply(TopLevelResource rsrc)
-      throws OrmException, RestApiException, PermissionBackendException {
+      throws RestApiException, PermissionBackendException {
     if (Strings.isNullOrEmpty(query)) {
       throw new BadRequestException("missing query field");
     }
diff --git a/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java b/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
index f4fa354..ee72ab7 100644
--- a/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -56,7 +55,7 @@
   @Override
   public DiffPreferencesInfo apply(AccountResource rsrc, DiffPreferencesInfo input)
       throws RestApiException, ConfigInvalidException, RepositoryNotFoundException, IOException,
-          PermissionBackendException, OrmException {
+          PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
     }
diff --git a/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java b/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
index 4e3f1d5..27d32f2 100644
--- a/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -57,7 +56,7 @@
   @Override
   public EditPreferencesInfo apply(AccountResource rsrc, EditPreferencesInfo input)
       throws RestApiException, RepositoryNotFoundException, IOException, ConfigInvalidException,
-          PermissionBackendException, OrmException {
+          PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
     }
diff --git a/java/com/google/gerrit/server/restapi/account/SetPreferences.java b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
index 2471689..c6623db 100644
--- a/java/com/google/gerrit/server/restapi/account/SetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -62,8 +61,7 @@
 
   @Override
   public GeneralPreferencesInfo apply(AccountResource rsrc, GeneralPreferencesInfo input)
-      throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException,
-          OrmException {
+      throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
     }
diff --git a/java/com/google/gerrit/server/restapi/account/SshKeys.java b/java/com/google/gerrit/server/restapi/account/SshKeys.java
index 4e44c71..6e3f905 100644
--- a/java/com/google/gerrit/server/restapi/account/SshKeys.java
+++ b/java/com/google/gerrit/server/restapi/account/SshKeys.java
@@ -28,7 +28,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -64,7 +63,7 @@
 
   @Override
   public AccountResource.SshKey parse(AccountResource rsrc, IdString id)
-      throws ResourceNotFoundException, OrmException, IOException, ConfigInvalidException,
+      throws ResourceNotFoundException, IOException, ConfigInvalidException,
           PermissionBackendException {
     if (!self.get().hasSameAccountId(rsrc.getUser())) {
       try {
diff --git a/java/com/google/gerrit/server/restapi/account/StarredChanges.java b/java/com/google/gerrit/server/restapi/account/StarredChanges.java
index a109815..3c14ad3 100644
--- a/java/com/google/gerrit/server/restapi/account/StarredChanges.java
+++ b/java/com/google/gerrit/server/restapi/account/StarredChanges.java
@@ -15,6 +15,8 @@
 package com.google.gerrit.server.restapi.account;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -40,8 +42,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.restapi.change.ChangesCollection;
 import com.google.gerrit.server.restapi.change.QueryChanges;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -68,7 +68,7 @@
 
   @Override
   public AccountResource.StarredChange parse(AccountResource parent, IdString id)
-      throws RestApiException, OrmException, PermissionBackendException, IOException {
+      throws RestApiException, PermissionBackendException, IOException {
     IdentifiedUser user = parent.getUser();
     ChangeResource change = changes.parse(TopLevelResource.INSTANCE, id);
     if (starredChangesUtil
@@ -114,7 +114,7 @@
 
     @Override
     public Response<?> apply(AccountResource rsrc, IdString id, EmptyInput in)
-        throws RestApiException, OrmException, IOException {
+        throws RestApiException, IOException {
       if (!self.get().hasSameAccountId(rsrc.getUser())) {
         throw new AuthException("not allowed to add starred change");
       }
@@ -124,7 +124,7 @@
         change = changes.parse(TopLevelResource.INSTANCE, id);
       } catch (ResourceNotFoundException e) {
         throw new UnprocessableEntityException(String.format("change %s not found", id.get()));
-      } catch (OrmException | PermissionBackendException | IOException e) {
+      } catch (StorageException | PermissionBackendException | IOException e) {
         logger.atSevere().withCause(e).log("cannot resolve change");
         throw new UnprocessableEntityException("internal server error");
       }
@@ -140,7 +140,7 @@
         throw new ResourceConflictException(e.getMessage());
       } catch (IllegalLabelException e) {
         throw new BadRequestException(e.getMessage());
-      } catch (OrmDuplicateKeyException e) {
+      } catch (DuplicateKeyException e) {
         return Response.none();
       }
       return Response.none();
@@ -179,7 +179,7 @@
 
     @Override
     public Response<?> apply(AccountResource.StarredChange rsrc, EmptyInput in)
-        throws AuthException, OrmException, IOException, IllegalLabelException {
+        throws AuthException, IOException, IllegalLabelException {
       if (!self.get().hasSameAccountId(rsrc.getUser())) {
         throw new AuthException("not allowed remove starred change");
       }
diff --git a/java/com/google/gerrit/server/restapi/account/Stars.java b/java/com/google/gerrit/server/restapi/account/Stars.java
index 5c4c4d5..c610adf 100644
--- a/java/com/google/gerrit/server/restapi/account/Stars.java
+++ b/java/com/google/gerrit/server/restapi/account/Stars.java
@@ -36,7 +36,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.restapi.change.ChangesCollection;
 import com.google.gerrit.server.restapi.change.QueryChanges;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -68,7 +67,7 @@
 
   @Override
   public Star parse(AccountResource parent, IdString id)
-      throws RestApiException, OrmException, PermissionBackendException, IOException {
+      throws RestApiException, PermissionBackendException, IOException {
     IdentifiedUser user = parent.getUser();
     ChangeResource change = changes.parse(TopLevelResource.INSTANCE, id);
     Set<String> labels = starredChangesUtil.getLabels(user.getAccountId(), change.getId());
@@ -99,7 +98,7 @@
     @Override
     @SuppressWarnings("unchecked")
     public List<ChangeInfo> apply(AccountResource rsrc)
-        throws BadRequestException, AuthException, OrmException, PermissionBackendException {
+        throws BadRequestException, AuthException, PermissionBackendException {
       if (!self.get().hasSameAccountId(rsrc.getUser())) {
         throw new AuthException("not allowed to list stars of another account");
       }
@@ -121,7 +120,7 @@
     }
 
     @Override
-    public SortedSet<String> apply(AccountResource.Star rsrc) throws AuthException, OrmException {
+    public SortedSet<String> apply(AccountResource.Star rsrc) throws AuthException {
       if (!self.get().hasSameAccountId(rsrc.getUser())) {
         throw new AuthException("not allowed to get stars of another account");
       }
@@ -142,7 +141,7 @@
 
     @Override
     public Collection<String> apply(AccountResource.Star rsrc, StarsInput in)
-        throws AuthException, BadRequestException, OrmException {
+        throws AuthException, BadRequestException {
       if (!self.get().hasSameAccountId(rsrc.getUser())) {
         throw new AuthException("not allowed to update stars of another account");
       }
diff --git a/java/com/google/gerrit/server/restapi/change/Abandon.java b/java/com/google/gerrit/server/restapi/change/Abandon.java
index 15dc9e5..c3327e4 100644
--- a/java/com/google/gerrit/server/restapi/change/Abandon.java
+++ b/java/com/google/gerrit/server/restapi/change/Abandon.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.AbandonInput;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -36,7 +37,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -69,8 +69,8 @@
   @Override
   protected ChangeInfo applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, AbandonInput input)
-      throws RestApiException, UpdateException, OrmException, PermissionBackendException,
-          IOException, ConfigInvalidException {
+      throws RestApiException, UpdateException, PermissionBackendException, IOException,
+          ConfigInvalidException {
     // Not allowed to abandon if the current patch set is locked.
     patchSetUtil.checkPatchSetNotLocked(rsrc.getNotes());
 
@@ -145,7 +145,7 @@
       if (patchSetUtil.isPatchSetLocked(rsrc.getNotes())) {
         return description;
       }
-    } catch (OrmException | IOException e) {
+    } catch (StorageException | IOException e) {
       logger.atSevere().withCause(e).log(
           "Failed to check if the current patch set of change %s is locked", change.getId());
       return description;
diff --git a/java/com/google/gerrit/server/restapi/change/ApplyFix.java b/java/com/google/gerrit/server/restapi/change/ApplyFix.java
index e4940ec..231d356 100644
--- a/java/com/google/gerrit/server/restapi/change/ApplyFix.java
+++ b/java/com/google/gerrit/server/restapi/change/ApplyFix.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.server.project.InvalidChangeOperationException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -67,8 +66,8 @@
 
   @Override
   public Response<EditInfo> apply(FixResource fixResource, Void nothing)
-      throws AuthException, OrmException, ResourceConflictException, IOException,
-          ResourceNotFoundException, PermissionBackendException {
+      throws AuthException, ResourceConflictException, IOException, ResourceNotFoundException,
+          PermissionBackendException {
     RevisionResource revisionResource = fixResource.getRevisionResource();
     Project.NameKey project = revisionResource.getProject();
     ProjectState projectState = projectCache.checkedGet(project);
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
index cabb30d..424f4d7 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeEdits.java
@@ -54,7 +54,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.InvalidChangeOperationException;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -95,7 +94,7 @@
 
   @Override
   public ChangeEditResource parse(ChangeResource rsrc, IdString id)
-      throws ResourceNotFoundException, AuthException, IOException, OrmException {
+      throws ResourceNotFoundException, AuthException, IOException {
     Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser());
     if (!edit.isPresent()) {
       throw new ResourceNotFoundException(id);
@@ -119,8 +118,7 @@
 
     @Override
     public Response<?> apply(ChangeResource resource, IdString id, Put.Input input)
-        throws AuthException, ResourceConflictException, IOException, OrmException,
-            PermissionBackendException {
+        throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
       putEdit.apply(resource, id.get(), input.content);
       return Response.none();
     }
@@ -137,8 +135,7 @@
 
     @Override
     public Response<?> apply(ChangeResource rsrc, IdString id, Input in)
-        throws IOException, AuthException, ResourceConflictException, OrmException,
-            PermissionBackendException {
+        throws IOException, AuthException, ResourceConflictException, PermissionBackendException {
       return deleteContent.apply(rsrc, id.get());
     }
   }
@@ -184,8 +181,7 @@
 
     @Override
     public Response<EditInfo> apply(ChangeResource rsrc)
-        throws AuthException, IOException, ResourceNotFoundException, OrmException,
-            PermissionBackendException {
+        throws AuthException, IOException, ResourceNotFoundException, PermissionBackendException {
       Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser());
       if (!edit.isPresent()) {
         return Response.none();
@@ -240,8 +236,7 @@
 
     @Override
     public Response<?> apply(ChangeResource resource, Post.Input input)
-        throws AuthException, IOException, ResourceConflictException, OrmException,
-            PermissionBackendException {
+        throws AuthException, IOException, ResourceConflictException, PermissionBackendException {
       Project.NameKey project = resource.getProject();
       try (Repository repository = repositoryManager.openRepository(project)) {
         if (isRestoreFile(input)) {
@@ -286,14 +281,12 @@
 
     @Override
     public Response<?> apply(ChangeEditResource rsrc, Input input)
-        throws AuthException, ResourceConflictException, IOException, OrmException,
-            PermissionBackendException {
+        throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
       return apply(rsrc.getChangeResource(), rsrc.getPath(), input.content);
     }
 
     public Response<?> apply(ChangeResource rsrc, String path, RawInput newContent)
-        throws ResourceConflictException, AuthException, IOException, OrmException,
-            PermissionBackendException {
+        throws ResourceConflictException, AuthException, IOException, PermissionBackendException {
       if (Strings.isNullOrEmpty(path) || path.charAt(0) == '/') {
         throw new ResourceConflictException("Invalid path: " + path);
       }
@@ -327,14 +320,12 @@
 
     @Override
     public Response<?> apply(ChangeEditResource rsrc, Input input)
-        throws AuthException, ResourceConflictException, OrmException, IOException,
-            PermissionBackendException {
+        throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
       return apply(rsrc.getChangeResource(), rsrc.getPath());
     }
 
     public Response<?> apply(ChangeResource rsrc, String filePath)
-        throws AuthException, IOException, OrmException, ResourceConflictException,
-            PermissionBackendException {
+        throws AuthException, IOException, ResourceConflictException, PermissionBackendException {
       try (Repository repository = repositoryManager.openRepository(rsrc.getProject())) {
         editModifier.deleteFile(repository, rsrc.getNotes(), filePath);
       } catch (InvalidChangeOperationException e) {
@@ -429,7 +420,7 @@
     @Override
     public Object apply(ChangeResource rsrc, Input input)
         throws AuthException, IOException, BadRequestException, ResourceConflictException,
-            OrmException, PermissionBackendException {
+            PermissionBackendException {
       if (input == null || Strings.isNullOrEmpty(input.message)) {
         throw new BadRequestException("commit message must be provided");
       }
@@ -463,7 +454,7 @@
 
     @Override
     public BinaryResult apply(ChangeResource rsrc)
-        throws AuthException, IOException, ResourceNotFoundException, OrmException {
+        throws AuthException, IOException, ResourceNotFoundException {
       Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser());
       String msg;
       if (edit.isPresent()) {
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java b/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
index 59b3111..6ec4fdb 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.server.PatchSetUtil;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.change.IncludedIn;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -38,8 +37,7 @@
   }
 
   @Override
-  public IncludedInInfo apply(ChangeResource rsrc)
-      throws RestApiException, OrmException, IOException {
+  public IncludedInInfo apply(ChangeResource rsrc) throws RestApiException, IOException {
     PatchSet ps = psUtil.current(rsrc.getNotes());
     return includedIn.apply(rsrc.getProject(), ps.getRevision().get());
   }
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeMessages.java b/java/com/google/gerrit/server/restapi/change/ChangeMessages.java
index 25fc350..96c517f 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeMessages.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeMessages.java
@@ -23,7 +23,6 @@
 import com.google.gerrit.server.change.ChangeMessageResource;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.List;
@@ -52,7 +51,7 @@
 
   @Override
   public ChangeMessageResource parse(ChangeResource parent, IdString id)
-      throws OrmException, ResourceNotFoundException, PermissionBackendException {
+      throws ResourceNotFoundException, PermissionBackendException {
     String uuid = id.get();
 
     List<ChangeMessageInfo> changeMessages = listChangeMessages.apply(parent);
diff --git a/java/com/google/gerrit/server/restapi/change/ChangesCollection.java b/java/com/google/gerrit/server/restapi/change/ChangesCollection.java
index 9972195..9f2a52c 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangesCollection.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangesCollection.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -81,7 +80,7 @@
 
   @Override
   public ChangeResource parse(TopLevelResource root, IdString id)
-      throws RestApiException, OrmException, PermissionBackendException, IOException {
+      throws RestApiException, PermissionBackendException, IOException {
     List<ChangeNotes> notes = changeFinder.find(id.encoded(), true);
     if (notes.isEmpty()) {
       throw new ResourceNotFoundException(id);
@@ -98,8 +97,8 @@
   }
 
   public ChangeResource parse(Change.Id id)
-      throws ResourceConflictException, ResourceNotFoundException, OrmException,
-          PermissionBackendException, IOException {
+      throws ResourceConflictException, ResourceNotFoundException, PermissionBackendException,
+          IOException {
     List<ChangeNotes> notes = changeFinder.find(id);
     if (notes.isEmpty()) {
       throw new ResourceNotFoundException(toIdString(id));
diff --git a/java/com/google/gerrit/server/restapi/change/Check.java b/java/com/google/gerrit/server/restapi/change/Check.java
index dd0ce10..f62aa5a 100644
--- a/java/com/google/gerrit/server/restapi/change/Check.java
+++ b/java/com/google/gerrit/server/restapi/change/Check.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -47,14 +46,13 @@
   }
 
   @Override
-  public Response<ChangeInfo> apply(ChangeResource rsrc) throws RestApiException, OrmException {
+  public Response<ChangeInfo> apply(ChangeResource rsrc) throws RestApiException {
     return Response.withMustRevalidate(newChangeJson().format(rsrc));
   }
 
   @Override
   public Response<ChangeInfo> apply(ChangeResource rsrc, FixInput input)
-      throws RestApiException, OrmException, PermissionBackendException, NoSuchProjectException,
-          IOException {
+      throws RestApiException, PermissionBackendException, NoSuchProjectException, IOException {
     PermissionBackend.WithUser perm = permissionBackend.currentUser();
     if (!rsrc.isUserOwner()) {
       try {
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPick.java b/java/com/google/gerrit/server/restapi/change/CherryPick.java
index b68122e..72781c3 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPick.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPick.java
@@ -41,7 +41,6 @@
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -78,8 +77,8 @@
   @Override
   public CherryPickChangeInfo applyImpl(
       BatchUpdate.Factory updateFactory, RevisionResource rsrc, CherryPickInput input)
-      throws OrmException, IOException, UpdateException, RestApiException,
-          PermissionBackendException, ConfigInvalidException, NoSuchProjectException {
+      throws IOException, UpdateException, RestApiException, PermissionBackendException,
+          ConfigInvalidException, NoSuchProjectException {
     input.parent = input.parent == null ? 1 : input.parent;
     if (input.message == null || input.message.trim().isEmpty()) {
       throw new BadRequestException("message must be non-empty");
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
index 52334ed..19a9233 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -59,7 +59,6 @@
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -141,8 +140,8 @@
       PatchSet patch,
       CherryPickInput input,
       Branch.NameKey dest)
-      throws OrmException, IOException, InvalidChangeOperationException, IntegrationException,
-          UpdateException, RestApiException, ConfigInvalidException, NoSuchProjectException {
+      throws IOException, InvalidChangeOperationException, IntegrationException, UpdateException,
+          RestApiException, ConfigInvalidException, NoSuchProjectException {
     return cherryPick(
         batchUpdateFactory,
         change,
@@ -159,8 +158,8 @@
       ObjectId sourceCommit,
       CherryPickInput input,
       Branch.NameKey dest)
-      throws OrmException, IOException, InvalidChangeOperationException, IntegrationException,
-          UpdateException, RestApiException, ConfigInvalidException, NoSuchProjectException {
+      throws IOException, InvalidChangeOperationException, IntegrationException, UpdateException,
+          RestApiException, ConfigInvalidException, NoSuchProjectException {
 
     IdentifiedUser identifiedUser = user.get();
     try (Repository git = gitManager.openRepository(project);
@@ -275,7 +274,7 @@
   }
 
   private RevCommit getBaseCommit(Ref destRef, String project, RevWalk revWalk, String base)
-      throws RestApiException, IOException, OrmException {
+      throws RestApiException, IOException {
     RevCommit destRefTip = revWalk.parseCommit(destRef.getObjectId());
     // The tip commit of the destination ref is the default base for the newly created change.
     if (Strings.isNullOrEmpty(base)) {
@@ -336,7 +335,7 @@
       @Nullable Change sourceChange,
       ObjectId sourceCommit,
       CherryPickInput input)
-      throws OrmException, IOException {
+      throws IOException {
     Change.Id changeId = new Change.Id(seq.nextChangeId());
     ChangeInserter ins = changeInserterFactory.create(changeId, cherryPickCommit, refName);
     Branch.NameKey sourceBranch = sourceChange == null ? null : sourceChange.getDest();
@@ -363,7 +362,7 @@
   }
 
   private NotifyResolver.Result resolveNotify(CherryPickInput input)
-      throws BadRequestException, OrmException, ConfigInvalidException, IOException {
+      throws BadRequestException, ConfigInvalidException, IOException {
     return notifyResolver.resolve(
         firstNonNull(input.notify, NotifyHandling.ALL), input.notifyDetails);
   }
@@ -382,10 +381,10 @@
     stringBuilder.append(".");
 
     if (!cherryPickCommit.getFilesWithGitConflicts().isEmpty()) {
-      stringBuilder.append("\n\nThe following files contain Git conflicts:\n");
+      stringBuilder.append("\n\nThe following files contain Git conflicts:");
       cherryPickCommit.getFilesWithGitConflicts().stream()
           .sorted()
-          .forEach(filePath -> stringBuilder.append("* ").append(filePath).append("\n"));
+          .forEach(filePath -> stringBuilder.append("\n* ").append(filePath));
     }
 
     return stringBuilder.toString();
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
index f76689c..f34f178 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
@@ -37,7 +37,6 @@
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -73,8 +72,8 @@
   @Override
   public CherryPickChangeInfo applyImpl(
       BatchUpdate.Factory updateFactory, CommitResource rsrc, CherryPickInput input)
-      throws OrmException, IOException, UpdateException, RestApiException,
-          PermissionBackendException, ConfigInvalidException, NoSuchProjectException {
+      throws IOException, UpdateException, RestApiException, PermissionBackendException,
+          ConfigInvalidException, NoSuchProjectException {
     RevCommit commit = rsrc.getCommit();
     String message = Strings.nullToEmpty(input.message).trim();
     input.message = message.isEmpty() ? commit.getFullMessage() : message;
diff --git a/java/com/google/gerrit/server/restapi/change/Comments.java b/java/com/google/gerrit/server/restapi/change/Comments.java
index 22f376b..d9a0d57 100644
--- a/java/com/google/gerrit/server/restapi/change/Comments.java
+++ b/java/com/google/gerrit/server/restapi/change/Comments.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.server.change.CommentResource;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -55,8 +54,7 @@
   }
 
   @Override
-  public CommentResource parse(RevisionResource rev, IdString id)
-      throws ResourceNotFoundException, OrmException {
+  public CommentResource parse(RevisionResource rev, IdString id) throws ResourceNotFoundException {
     String uuid = id.get();
     ChangeNotes notes = rev.getNotes();
 
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index 9395d9d..2326027 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -71,7 +71,6 @@
 import com.google.gerrit.server.update.RetryingRestCollectionModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -159,8 +158,8 @@
   @Override
   protected Response<ChangeInfo> applyImpl(
       BatchUpdate.Factory updateFactory, TopLevelResource parent, ChangeInput input)
-      throws OrmException, IOException, InvalidChangeOperationException, RestApiException,
-          UpdateException, PermissionBackendException, ConfigInvalidException {
+      throws IOException, InvalidChangeOperationException, RestApiException, UpdateException,
+          PermissionBackendException, ConfigInvalidException {
     IdentifiedUser me = user.get().asIdentifiedUser();
     checkAndSanitizeChangeInput(input, me);
 
@@ -267,8 +266,8 @@
       IdentifiedUser me,
       ProjectState projectState,
       BatchUpdate.Factory updateFactory)
-      throws RestApiException, OrmException, PermissionBackendException, IOException,
-          ConfigInvalidException, UpdateException {
+      throws RestApiException, PermissionBackendException, IOException, ConfigInvalidException,
+          UpdateException {
     try (Repository git = gitManager.openRepository(projectState.getNameKey());
         ObjectInserter oi = git.newObjectInserter();
         ObjectReader reader = oi.newReader();
@@ -320,7 +319,7 @@
   }
 
   private ChangeNotes getBaseChange(String baseChange)
-      throws OrmException, UnprocessableEntityException, PermissionBackendException {
+      throws UnprocessableEntityException, PermissionBackendException {
     List<ChangeNotes> notes = changeFinder.find(baseChange);
     if (notes.size() != 1) {
       throw new UnprocessableEntityException("Base change not found: " + baseChange);
@@ -442,7 +441,7 @@
       MergeInput merge,
       PersonIdent authorIdent,
       String commitMessage)
-      throws RestApiException, IOException, OrmException {
+      throws RestApiException, IOException {
     if (Strings.isNullOrEmpty(merge.source)) {
       throw new BadRequestException("merge.source must be non-empty");
     }
diff --git a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
index fa8adc0..b6e7628 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateDraftComment.java
@@ -41,7 +41,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -72,7 +71,7 @@
   @Override
   protected Response<CommentInfo> applyImpl(
       BatchUpdate.Factory updateFactory, RevisionResource rsrc, DraftInput in)
-      throws RestApiException, UpdateException, OrmException, PermissionBackendException {
+      throws RestApiException, UpdateException, PermissionBackendException {
     if (Strings.isNullOrEmpty(in.path)) {
       throw new BadRequestException("path must be non-empty");
     } else if (in.message == null || in.message.trim().isEmpty()) {
@@ -106,7 +105,7 @@
 
     @Override
     public boolean updateChange(ChangeContext ctx)
-        throws ResourceNotFoundException, OrmException, UnprocessableEntityException,
+        throws ResourceNotFoundException, UnprocessableEntityException,
             PatchListNotAvailableException {
       PatchSet ps = psUtil.get(ctx.getNotes(), psId);
       if (ps == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
index 6df490c..d3e737f 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
@@ -57,7 +57,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -121,8 +120,7 @@
   @Override
   protected Response<ChangeInfo> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, MergePatchSetInput in)
-      throws OrmException, IOException, RestApiException, UpdateException,
-          PermissionBackendException {
+      throws IOException, RestApiException, UpdateException, PermissionBackendException {
     // Not allowed to create a new patch set if the current patch set is locked.
     psUtil.checkPatchSetNotLocked(rsrc.getNotes());
 
@@ -200,7 +198,7 @@
   }
 
   private PatchSet findBasePatchSet(String baseChange)
-      throws PermissionBackendException, OrmException, UnprocessableEntityException {
+      throws PermissionBackendException, UnprocessableEntityException {
     List<ChangeNotes> notes = changeFinder.find(baseChange);
     if (notes.size() != 1) {
       throw new UnprocessableEntityException("Base change not found: " + baseChange);
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
index 907347f..02387be 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
@@ -38,7 +38,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -68,7 +67,7 @@
   @Override
   protected Response<AccountInfo> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
-      throws RestApiException, UpdateException, OrmException, PermissionBackendException {
+      throws RestApiException, UpdateException, PermissionBackendException {
     rsrc.permissions().check(ChangePermission.EDIT_ASSIGNEE);
 
     try (BatchUpdate bu =
@@ -88,7 +87,7 @@
     private AccountState deletedAssignee;
 
     @Override
-    public boolean updateChange(ChangeContext ctx) throws RestApiException, OrmException {
+    public boolean updateChange(ChangeContext ctx) throws RestApiException {
       change = ctx.getChange();
       ChangeUpdate update = ctx.getUpdate(change.currentPatchSetId());
       Account.Id currentAssigneeId = change.getAssignee();
@@ -120,7 +119,7 @@
     }
 
     @Override
-    public void postUpdate(Context ctx) throws OrmException {
+    public void postUpdate(Context ctx) {
       assigneeChanged.fire(change, ctx.getAccount(), deletedAssignee, ctx.getWhen());
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteChangeEdit.java b/java/com/google/gerrit/server/restapi/change/DeleteChangeEdit.java
index d49a804..f7f808a 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChangeEdit.java
@@ -23,7 +23,6 @@
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.edit.ChangeEdit;
 import com.google.gerrit.server.edit.ChangeEditUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -41,7 +40,7 @@
 
   @Override
   public Response<?> apply(ChangeResource rsrc, Input input)
-      throws AuthException, ResourceNotFoundException, IOException, OrmException {
+      throws AuthException, ResourceNotFoundException, IOException {
     Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser());
     if (edit.isPresent()) {
       editUtil.delete(edit.get());
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java b/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
index 5dcd1fa..0fb8e18 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChangeMessage.java
@@ -43,7 +43,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -83,8 +82,7 @@
       BatchUpdate.Factory updateFactory,
       ChangeMessageResource resource,
       DeleteChangeMessageInput input)
-      throws RestApiException, PermissionBackendException, OrmException, UpdateException,
-          IOException {
+      throws RestApiException, PermissionBackendException, UpdateException, IOException {
     CurrentUser user = userProvider.get();
     permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
 
@@ -103,7 +101,7 @@
   }
 
   private ChangeMessageInfo createUpdatedChangeMessageInfo(Change.Id id, int targetIdx)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     List<ChangeMessage> messages = changeMessagesUtil.byChange(notesFactory.createChecked(id));
     ChangeMessage updatedChangeMessage = messages.get(targetIdx);
     AccountLoader accountLoader = accountLoaderFactory.create(true);
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteComment.java b/java/com/google/gerrit/server/restapi/change/DeleteComment.java
index e73c4f3..30a8efd7 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteComment.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteComment.java
@@ -36,7 +36,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -74,8 +73,8 @@
   @Override
   public CommentInfo applyImpl(
       BatchUpdate.Factory batchUpdateFactory, CommentResource rsrc, DeleteCommentInput input)
-      throws RestApiException, IOException, ConfigInvalidException, OrmException,
-          PermissionBackendException, UpdateException {
+      throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException,
+          UpdateException {
     CurrentUser user = userProvider.get();
     permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
 
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java b/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
index bcff6e4..de04d36 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteDraftComment.java
@@ -35,7 +35,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.Collections;
@@ -83,7 +82,7 @@
 
     @Override
     public boolean updateChange(ChangeContext ctx)
-        throws ResourceNotFoundException, OrmException, PatchListNotAvailableException {
+        throws ResourceNotFoundException, PatchListNotAvailableException {
       Optional<Comment> maybeComment =
           commentsUtil.getDraft(ctx.getNotes(), ctx.getIdentifiedUser(), key);
       if (!maybeComment.isPresent()) {
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteVote.java b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
index 3a167bf..77894fb 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteVote.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteVote.java
@@ -56,7 +56,6 @@
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.LabelVote;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -105,7 +104,7 @@
   @Override
   protected Response<?> applyImpl(
       BatchUpdate.Factory updateFactory, VoteResource rsrc, DeleteVoteInput input)
-      throws RestApiException, UpdateException, IOException, OrmException, ConfigInvalidException {
+      throws RestApiException, UpdateException, IOException, ConfigInvalidException {
     if (input == null) {
       input = new DeleteVoteInput();
     }
@@ -163,8 +162,7 @@
 
     @Override
     public boolean updateChange(ChangeContext ctx)
-        throws OrmException, AuthException, ResourceNotFoundException, IOException,
-            PermissionBackendException {
+        throws AuthException, ResourceNotFoundException, IOException, PermissionBackendException {
       change = ctx.getChange();
       PatchSet.Id psId = change.currentPatchSetId();
       ps = psUtil.current(ctx.getNotes());
diff --git a/java/com/google/gerrit/server/restapi/change/DownloadContent.java b/java/com/google/gerrit/server/restapi/change/DownloadContent.java
index b6564c0..1022cad 100644
--- a/java/com/google/gerrit/server/restapi/change/DownloadContent.java
+++ b/java/com/google/gerrit/server/restapi/change/DownloadContent.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import org.eclipse.jgit.lib.ObjectId;
@@ -43,7 +42,7 @@
 
   @Override
   public BinaryResult apply(FileResource rsrc)
-      throws ResourceNotFoundException, IOException, NoSuchChangeException, OrmException {
+      throws ResourceNotFoundException, IOException, NoSuchChangeException {
     String path = rsrc.getPatchKey().get();
     RevisionResource rev = rsrc.getRevision();
     ObjectId revstr = ObjectId.fromString(rev.getPatchSet().getRevision().get());
diff --git a/java/com/google/gerrit/server/restapi/change/DraftComments.java b/java/com/google/gerrit/server/restapi/change/DraftComments.java
index 9f06252..dd61ca0 100644
--- a/java/com/google/gerrit/server/restapi/change/DraftComments.java
+++ b/java/com/google/gerrit/server/restapi/change/DraftComments.java
@@ -25,7 +25,6 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.change.DraftCommentResource;
 import com.google.gerrit.server.change.RevisionResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -62,7 +61,7 @@
 
   @Override
   public DraftCommentResource parse(RevisionResource rev, IdString id)
-      throws ResourceNotFoundException, OrmException, AuthException {
+      throws ResourceNotFoundException, AuthException {
     checkIdentifiedUser();
     String uuid = id.get();
     for (Comment c :
diff --git a/java/com/google/gerrit/server/restapi/change/Files.java b/java/com/google/gerrit/server/restapi/change/Files.java
index 5f2c370..c30c0ba 100644
--- a/java/com/google/gerrit/server/restapi/change/Files.java
+++ b/java/com/google/gerrit/server/restapi/change/Files.java
@@ -19,6 +19,7 @@
 import com.google.common.hash.Hasher;
 import com.google.common.hash.Hashing;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.common.FileInfo;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -49,7 +50,6 @@
 import com.google.gerrit.server.patch.PatchListObjectTooLargeException;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.plugincontext.PluginItemContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -146,7 +146,7 @@
 
     @Override
     public Response<?> apply(RevisionResource resource)
-        throws AuthException, BadRequestException, ResourceNotFoundException, OrmException,
+        throws AuthException, BadRequestException, ResourceNotFoundException,
             RepositoryNotFoundException, IOException, PatchListNotAvailableException,
             PermissionBackendException {
       checkOptions();
@@ -223,8 +223,7 @@
       }
     }
 
-    private Collection<String> reviewed(RevisionResource resource)
-        throws AuthException, OrmException {
+    private Collection<String> reviewed(RevisionResource resource) throws AuthException {
       CurrentUser user = self.get();
       if (!(user.isIdentifiedUser())) {
         throw new AuthException("Authentication required");
@@ -235,7 +234,7 @@
       Optional<PatchSetWithReviewedFiles> o;
       o =
           accountPatchReviewStore.call(
-              s -> s.findReviewed(patchSetId.getId(), userId), OrmException.class);
+              s -> s.findReviewed(patchSetId.getId(), userId), StorageException.class);
 
       if (o.isPresent()) {
         PatchSetWithReviewedFiles res = o.get();
@@ -257,7 +256,7 @@
 
     private List<String> copy(
         Set<String> paths, PatchSet.Id old, RevisionResource resource, Account.Id userId)
-        throws IOException, PatchListNotAvailableException, OrmException {
+        throws IOException, PatchListNotAvailableException {
       Project.NameKey project = resource.getChange().getProject();
       try (Repository git = gitManager.openRepository(project);
           ObjectReader reader = git.newObjectReader();
@@ -319,7 +318,7 @@
 
         accountPatchReviewStore.run(
             s -> s.markReviewed(resource.getPatchSet().getId(), userId, pathList),
-            OrmException.class);
+            StorageException.class);
         return pathList;
       }
     }
diff --git a/java/com/google/gerrit/server/restapi/change/Fixes.java b/java/com/google/gerrit/server/restapi/change/Fixes.java
index 1d8726d..855d1f4 100644
--- a/java/com/google/gerrit/server/restapi/change/Fixes.java
+++ b/java/com/google/gerrit/server/restapi/change/Fixes.java
@@ -25,7 +25,6 @@
 import com.google.gerrit.server.change.FixResource;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.List;
@@ -50,7 +49,7 @@
 
   @Override
   public FixResource parse(RevisionResource revisionResource, IdString id)
-      throws ResourceNotFoundException, OrmException {
+      throws ResourceNotFoundException {
     String fixId = id.get();
     ChangeNotes changeNotes = revisionResource.getNotes();
 
diff --git a/java/com/google/gerrit/server/restapi/change/GetAssignee.java b/java/com/google/gerrit/server/restapi/change/GetAssignee.java
index e95f8d8..f89fe1b 100644
--- a/java/com/google/gerrit/server/restapi/change/GetAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/GetAssignee.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.Optional;
@@ -36,8 +35,7 @@
   }
 
   @Override
-  public Response<AccountInfo> apply(ChangeResource rsrc)
-      throws OrmException, PermissionBackendException {
+  public Response<AccountInfo> apply(ChangeResource rsrc) throws PermissionBackendException {
     Optional<Account.Id> assignee = Optional.ofNullable(rsrc.getChange().getAssignee());
     if (assignee.isPresent()) {
       return Response.ok(accountLoaderFactory.create(true).fillOne(assignee.get()));
diff --git a/java/com/google/gerrit/server/restapi/change/GetBlame.java b/java/com/google/gerrit/server/restapi/change/GetBlame.java
index c7a8015..bb92f00 100644
--- a/java/com/google/gerrit/server/restapi/change/GetBlame.java
+++ b/java/com/google/gerrit/server/restapi/change/GetBlame.java
@@ -32,7 +32,6 @@
 import com.google.gerrit.server.project.InvalidChangeOperationException;
 import com.google.gitiles.blame.cache.BlameCache;
 import com.google.gitiles.blame.cache.Region;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -79,7 +78,7 @@
 
   @Override
   public Response<List<BlameInfo>> apply(FileResource resource)
-      throws RestApiException, OrmException, IOException, InvalidChangeOperationException {
+      throws RestApiException, IOException, InvalidChangeOperationException {
     Project.NameKey project = resource.getRevision().getChange().getProject();
     try (Repository repository = repoManager.openRepository(project);
         ObjectInserter ins = repository.newObjectInserter();
diff --git a/java/com/google/gerrit/server/restapi/change/GetChange.java b/java/com/google/gerrit/server/restapi/change/GetChange.java
index c8641d5..c28741b 100644
--- a/java/com/google/gerrit/server/restapi/change/GetChange.java
+++ b/java/com/google/gerrit/server/restapi/change/GetChange.java
@@ -31,7 +31,6 @@
 import com.google.gerrit.server.change.PluginDefinedAttributesFactories;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -74,11 +73,11 @@
   }
 
   @Override
-  public Response<ChangeInfo> apply(ChangeResource rsrc) throws OrmException {
+  public Response<ChangeInfo> apply(ChangeResource rsrc) {
     return Response.withMustRevalidate(newChangeJson().format(rsrc));
   }
 
-  Response<ChangeInfo> apply(RevisionResource rsrc) throws OrmException {
+  Response<ChangeInfo> apply(RevisionResource rsrc) {
     return Response.withMustRevalidate(newChangeJson().format(rsrc));
   }
 
diff --git a/java/com/google/gerrit/server/restapi/change/GetComment.java b/java/com/google/gerrit/server/restapi/change/GetComment.java
index d067dff..0109c95 100644
--- a/java/com/google/gerrit/server/restapi/change/GetComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetComment.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.CommentResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -34,7 +33,7 @@
   }
 
   @Override
-  public CommentInfo apply(CommentResource rsrc) throws OrmException, PermissionBackendException {
+  public CommentInfo apply(CommentResource rsrc) throws PermissionBackendException {
     return commentJson.get().newCommentFormatter().format(rsrc.getComment());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/GetContent.java b/java/com/google/gerrit/server/restapi/change/GetContent.java
index c133581..1d35ab5 100644
--- a/java/com/google/gerrit/server/restapi/change/GetContent.java
+++ b/java/com/google/gerrit/server/restapi/change/GetContent.java
@@ -30,7 +30,6 @@
 import com.google.gerrit.server.patch.Text;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
@@ -63,7 +62,7 @@
 
   @Override
   public BinaryResult apply(FileResource rsrc)
-      throws ResourceNotFoundException, IOException, BadRequestException, OrmException {
+      throws ResourceNotFoundException, IOException, BadRequestException {
     String path = rsrc.getPatchKey().get();
     if (Patch.COMMIT_MSG.equals(path)) {
       String msg = getMessage(rsrc.getRevision().getChangeResource().getNotes());
@@ -83,7 +82,7 @@
         parent);
   }
 
-  private String getMessage(ChangeNotes notes) throws OrmException, IOException {
+  private String getMessage(ChangeNotes notes) throws IOException {
     Change.Id changeId = notes.getChangeId();
     PatchSet ps = psUtil.current(notes);
     if (ps == null) {
@@ -99,7 +98,7 @@
     }
   }
 
-  private byte[] getMergeList(ChangeNotes notes) throws OrmException, IOException {
+  private byte[] getMergeList(ChangeNotes notes) throws IOException {
     Change.Id changeId = notes.getChangeId();
     PatchSet ps = psUtil.current(notes);
     if (ps == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/GetDetail.java b/java/com/google/gerrit/server/restapi/change/GetDetail.java
index 06fdd99..e31d84b 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDetail.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDetail.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.server.DynamicOptions;
 import com.google.gerrit.server.DynamicOptions.DynamicBean;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import org.kohsuke.args4j.Option;
 
@@ -59,7 +58,7 @@
   }
 
   @Override
-  public Response<ChangeInfo> apply(ChangeResource rsrc) throws OrmException {
+  public Response<ChangeInfo> apply(ChangeResource rsrc) {
     return delegate.apply(rsrc);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/GetDiff.java b/java/com/google/gerrit/server/restapi/change/GetDiff.java
index 1fae739..761500a 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDiff.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDiff.java
@@ -56,7 +56,6 @@
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.List;
@@ -126,7 +125,7 @@
 
   @Override
   public Response<DiffInfo> apply(FileResource resource)
-      throws ResourceConflictException, ResourceNotFoundException, OrmException, AuthException,
+      throws ResourceConflictException, ResourceNotFoundException, AuthException,
           InvalidChangeOperationException, IOException, PermissionBackendException {
     DiffPreferencesInfo prefs = new DiffPreferencesInfo();
     if (whitespace != null) {
diff --git a/java/com/google/gerrit/server/restapi/change/GetDraftComment.java b/java/com/google/gerrit/server/restapi/change/GetDraftComment.java
index 6049607..ca5b56f 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDraftComment.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.DraftCommentResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -34,8 +33,7 @@
   }
 
   @Override
-  public CommentInfo apply(DraftCommentResource rsrc)
-      throws OrmException, PermissionBackendException {
+  public CommentInfo apply(DraftCommentResource rsrc) throws PermissionBackendException {
     return commentJson.get().newCommentFormatter().format(rsrc.getComment());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/GetHashtags.java b/java/com/google/gerrit/server/restapi/change/GetHashtags.java
index 8369acf..aff3a44 100644
--- a/java/com/google/gerrit/server/restapi/change/GetHashtags.java
+++ b/java/com/google/gerrit/server/restapi/change/GetHashtags.java
@@ -20,7 +20,6 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Singleton;
 import java.io.IOException;
 import java.util.Collections;
@@ -30,7 +29,7 @@
 public class GetHashtags implements RestReadView<ChangeResource> {
   @Override
   public Response<Set<String>> apply(ChangeResource req)
-      throws AuthException, OrmException, IOException, BadRequestException {
+      throws AuthException, IOException, BadRequestException {
     ChangeNotes notes = req.getNotes().load();
     Set<String> hashtags = notes.getHashtags();
     if (hashtags == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/GetPastAssignees.java b/java/com/google/gerrit/server/restapi/change/GetPastAssignees.java
index 279cfe3..1d56669 100644
--- a/java/com/google/gerrit/server/restapi/change/GetPastAssignees.java
+++ b/java/com/google/gerrit/server/restapi/change/GetPastAssignees.java
@@ -23,7 +23,6 @@
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.Collections;
@@ -40,8 +39,7 @@
   }
 
   @Override
-  public Response<List<AccountInfo>> apply(ChangeResource rsrc)
-      throws OrmException, PermissionBackendException {
+  public Response<List<AccountInfo>> apply(ChangeResource rsrc) throws PermissionBackendException {
 
     Set<Account.Id> pastAssignees = rsrc.getNotes().load().getPastAssignees();
     if (pastAssignees == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/GetPureRevert.java b/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
index dcafe56..fa5cc36 100644
--- a/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
+++ b/java/com/google/gerrit/server/restapi/change/GetPureRevert.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.change.PureRevert;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.Optional;
@@ -48,8 +47,7 @@
 
   @Override
   public PureRevertInfo apply(ChangeResource rsrc)
-      throws ResourceConflictException, IOException, BadRequestException, OrmException,
-          AuthException {
+      throws ResourceConflictException, IOException, BadRequestException, AuthException {
     boolean isPureRevert = pureRevert.get(rsrc.getNotes(), Optional.ofNullable(claimedOriginal));
     return new PureRevertInfo(isPureRevert);
   }
diff --git a/java/com/google/gerrit/server/restapi/change/GetRelated.java b/java/com/google/gerrit/server/restapi/change/GetRelated.java
index 9a605c0..332cc4d 100644
--- a/java/com/google/gerrit/server/restapi/change/GetRelated.java
+++ b/java/com/google/gerrit/server/restapi/change/GetRelated.java
@@ -36,7 +36,6 @@
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -70,7 +69,7 @@
 
   @Override
   public RelatedChangesInfo apply(RevisionResource rsrc)
-      throws RepositoryNotFoundException, IOException, OrmException, NoSuchProjectException,
+      throws RepositoryNotFoundException, IOException, NoSuchProjectException,
           PermissionBackendException {
     RelatedChangesInfo relatedChangesInfo = new RelatedChangesInfo();
     relatedChangesInfo.changes = getRelated(rsrc);
@@ -78,7 +77,7 @@
   }
 
   private List<RelatedChangeAndCommitInfo> getRelated(RevisionResource rsrc)
-      throws OrmException, IOException, PermissionBackendException {
+      throws IOException, PermissionBackendException {
     Set<String> groups = getAllGroups(rsrc.getNotes(), psUtil);
     if (groups.isEmpty()) {
       return Collections.emptyList();
@@ -123,12 +122,11 @@
   }
 
   @VisibleForTesting
-  public static Set<String> getAllGroups(ChangeNotes notes, PatchSetUtil psUtil)
-      throws OrmException {
+  public static Set<String> getAllGroups(ChangeNotes notes, PatchSetUtil psUtil) {
     return psUtil.byChange(notes).stream().flatMap(ps -> ps.getGroups().stream()).collect(toSet());
   }
 
-  private void reloadChangeIfStale(List<ChangeData> cds, PatchSet wantedPs) throws OrmException {
+  private void reloadChangeIfStale(List<ChangeData> cds, PatchSet wantedPs) {
     for (ChangeData cd : cds) {
       if (cd.getId().equals(wantedPs.getId().getParentKey())) {
         if (cd.patchSet(wantedPs.getId()) == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/GetReview.java b/java/com/google/gerrit/server/restapi/change/GetReview.java
index 40e132d..8d941ab 100644
--- a/java/com/google/gerrit/server/restapi/change/GetReview.java
+++ b/java/com/google/gerrit/server/restapi/change/GetReview.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.RevisionResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -35,7 +34,7 @@
   }
 
   @Override
-  public Response<ChangeInfo> apply(RevisionResource rsrc) throws OrmException {
+  public Response<ChangeInfo> apply(RevisionResource rsrc) {
     return delegate.apply(rsrc);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/GetReviewer.java b/java/com/google/gerrit/server/restapi/change/GetReviewer.java
index a11380b..73760da 100644
--- a/java/com/google/gerrit/server/restapi/change/GetReviewer.java
+++ b/java/com/google/gerrit/server/restapi/change/GetReviewer.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.server.change.ReviewerJson;
 import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.List;
@@ -34,8 +33,7 @@
   }
 
   @Override
-  public List<ReviewerInfo> apply(ReviewerResource rsrc)
-      throws OrmException, PermissionBackendException {
+  public List<ReviewerInfo> apply(ReviewerResource rsrc) throws PermissionBackendException {
     return json.format(rsrc);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/GetRevisionActions.java b/java/com/google/gerrit/server/restapi/change/GetRevisionActions.java
index b0b49a3..c4da3b6 100644
--- a/java/com/google/gerrit/server/restapi/change/GetRevisionActions.java
+++ b/java/com/google/gerrit/server/restapi/change/GetRevisionActions.java
@@ -16,6 +16,7 @@
 
 import com.google.common.hash.Hasher;
 import com.google.common.hash.Hashing;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.common.ActionInfo;
 import com.google.gerrit.extensions.restapi.ETagView;
 import com.google.gerrit.extensions.restapi.Response;
@@ -28,8 +29,6 @@
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.submit.ChangeSet;
 import com.google.gerrit.server.submit.MergeSuperSet;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -57,7 +56,7 @@
   }
 
   @Override
-  public Response<Map<String, ActionInfo>> apply(RevisionResource rsrc) throws OrmException {
+  public Response<Map<String, ActionInfo>> apply(RevisionResource rsrc) {
     return Response.withMustRevalidate(delegate.format(rsrc));
   }
 
@@ -73,8 +72,8 @@
         changeResourceFactory.create(cd.notes(), user).prepareETag(h, user);
       }
       h.putBoolean(cs.furtherHiddenChanges());
-    } catch (IOException | OrmException | PermissionBackendException e) {
-      throw new OrmRuntimeException(e);
+    } catch (IOException | PermissionBackendException e) {
+      throw new StorageException(e);
     }
     return h.hash().toString();
   }
diff --git a/java/com/google/gerrit/server/restapi/change/GetRobotComment.java b/java/com/google/gerrit/server/restapi/change/GetRobotComment.java
index 0197068..75d994d 100644
--- a/java/com/google/gerrit/server/restapi/change/GetRobotComment.java
+++ b/java/com/google/gerrit/server/restapi/change/GetRobotComment.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.change.RobotCommentResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -34,8 +33,7 @@
   }
 
   @Override
-  public RobotCommentInfo apply(RobotCommentResource rsrc)
-      throws OrmException, PermissionBackendException {
+  public RobotCommentInfo apply(RobotCommentResource rsrc) throws PermissionBackendException {
     return commentJson.get().newRobotCommentFormatter().format(rsrc.getComment());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/Ignore.java b/java/com/google/gerrit/server/restapi/change/Ignore.java
index e319451..25cf311 100644
--- a/java/com/google/gerrit/server/restapi/change/Ignore.java
+++ b/java/com/google/gerrit/server/restapi/change/Ignore.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -26,7 +27,6 @@
 import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
 import com.google.gerrit.server.StarredChangesUtil.MutuallyExclusiveLabelsException;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -51,7 +51,7 @@
 
   @Override
   public Response<String> apply(ChangeResource rsrc, Input input)
-      throws RestApiException, OrmException, IllegalLabelException {
+      throws RestApiException, IllegalLabelException {
     try {
       if (rsrc.isUserOwner()) {
         throw new BadRequestException("cannot ignore own change");
@@ -73,7 +73,7 @@
   private boolean isIgnored(ChangeResource rsrc) {
     try {
       return stars.isIgnored(rsrc);
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("failed to check ignored star");
     }
     return false;
diff --git a/java/com/google/gerrit/server/restapi/change/Index.java b/java/com/google/gerrit/server/restapi/change/Index.java
index f04a3e8..90dad98 100644
--- a/java/com/google/gerrit/server/restapi/change/Index.java
+++ b/java/com/google/gerrit/server/restapi/change/Index.java
@@ -25,7 +25,6 @@
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryingRestModifyView;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -45,7 +44,7 @@
   @Override
   protected Response<?> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
-      throws IOException, AuthException, OrmException, PermissionBackendException {
+      throws IOException, AuthException, PermissionBackendException {
     permissionBackend.currentUser().check(GlobalPermission.MAINTAIN_SERVER);
     indexer.index(rsrc.getChange());
     return Response.none();
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeComments.java b/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
index 902a67e..992f602 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeComments.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -46,7 +45,7 @@
 
   @Override
   public Map<String, List<CommentInfo>> apply(ChangeResource rsrc)
-      throws AuthException, OrmException, PermissionBackendException {
+      throws AuthException, PermissionBackendException {
     ChangeData cd = changeDataFactory.create(rsrc.getNotes());
     return commentJson
         .get()
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java b/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
index 7bdc139..1939385 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeDrafts.java
@@ -22,7 +22,6 @@
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -47,7 +46,7 @@
 
   @Override
   public Map<String, List<CommentInfo>> apply(ChangeResource rsrc)
-      throws AuthException, OrmException, PermissionBackendException {
+      throws AuthException, PermissionBackendException {
     if (!rsrc.getUser().isIdentifiedUser()) {
       throw new AuthException("Authentication required");
     }
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java b/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
index c658fc4..a2e3d4b 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeMessages.java
@@ -23,7 +23,6 @@
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.List;
@@ -42,8 +41,7 @@
   }
 
   @Override
-  public List<ChangeMessageInfo> apply(ChangeResource resource)
-      throws OrmException, PermissionBackendException {
+  public List<ChangeMessageInfo> apply(ChangeResource resource) throws PermissionBackendException {
     List<ChangeMessage> messages = changeMessagesUtil.byChange(resource.getNotes());
     List<ChangeMessageInfo> messageInfos =
         messages.stream()
diff --git a/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java b/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
index 3ea2ac2..e5840fd 100644
--- a/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListChangeRobotComments.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.util.List;
@@ -44,7 +43,7 @@
 
   @Override
   public Map<String, List<RobotCommentInfo>> apply(ChangeResource rsrc)
-      throws AuthException, OrmException, PermissionBackendException {
+      throws AuthException, PermissionBackendException {
     ChangeData cd = changeDataFactory.create(rsrc.getNotes());
     return commentJson
         .get()
diff --git a/java/com/google/gerrit/server/restapi/change/ListReviewers.java b/java/com/google/gerrit/server/restapi/change/ListReviewers.java
index 65c8d45..12732ff 100644
--- a/java/com/google/gerrit/server/restapi/change/ListReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/ListReviewers.java
@@ -23,7 +23,6 @@
 import com.google.gerrit.server.change.ReviewerJson;
 import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.LinkedHashMap;
@@ -45,8 +44,7 @@
   }
 
   @Override
-  public List<ReviewerInfo> apply(ChangeResource rsrc)
-      throws OrmException, PermissionBackendException {
+  public List<ReviewerInfo> apply(ChangeResource rsrc) throws PermissionBackendException {
     Map<String, ReviewerResource> reviewers = new LinkedHashMap<>();
     for (Account.Id accountId : approvalsUtil.getReviewers(rsrc.getNotes()).all()) {
       if (!reviewers.containsKey(accountId.toString())) {
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java b/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
index f10d92b..b39ba63 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionComments.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -36,7 +35,7 @@
   }
 
   @Override
-  protected Iterable<Comment> listComments(RevisionResource rsrc) throws OrmException {
+  protected Iterable<Comment> listComments(RevisionResource rsrc) {
     ChangeNotes notes = rsrc.getNotes();
     return commentsUtil.publishedByPatchSet(notes, rsrc.getPatchSet().getId());
   }
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
index 3df7e9c..a46bd6c 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionDrafts.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -39,7 +38,7 @@
     this.commentsUtil = commentsUtil;
   }
 
-  protected Iterable<Comment> listComments(RevisionResource rsrc) throws OrmException {
+  protected Iterable<Comment> listComments(RevisionResource rsrc) {
     return commentsUtil.draftByPatchSetAuthor(
         rsrc.getPatchSet().getId(), rsrc.getAccountId(), rsrc.getNotes());
   }
@@ -50,7 +49,7 @@
 
   @Override
   public Map<String, List<CommentInfo>> apply(RevisionResource rsrc)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     return commentJson
         .get()
         .setFillAccounts(includeAuthorInfo())
@@ -59,7 +58,7 @@
   }
 
   public ImmutableList<CommentInfo> getComments(RevisionResource rsrc)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     return commentJson
         .get()
         .setFillAccounts(includeAuthorInfo())
diff --git a/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java b/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
index 6e7ffd9..920cde9 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRevisionReviewers.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.LinkedHashMap;
@@ -47,7 +46,7 @@
 
   @Override
   public List<ReviewerInfo> apply(RevisionResource rsrc)
-      throws OrmException, MethodNotAllowedException, PermissionBackendException {
+      throws MethodNotAllowedException, PermissionBackendException {
     if (!rsrc.isCurrent()) {
       throw new MethodNotAllowedException("Cannot list reviewers on non-current patch set");
     }
diff --git a/java/com/google/gerrit/server/restapi/change/ListRobotComments.java b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
index c13b1e7..bbf46a3 100644
--- a/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/ListRobotComments.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.server.CommentsUtil;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -41,7 +40,7 @@
 
   @Override
   public Map<String, List<RobotCommentInfo>> apply(RevisionResource rsrc)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     return commentJson
         .get()
         .setFillAccounts(true)
@@ -50,7 +49,7 @@
   }
 
   public ImmutableList<RobotCommentInfo> getComments(RevisionResource rsrc)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     return commentJson
         .get()
         .setFillAccounts(true)
@@ -58,7 +57,7 @@
         .formatAsList(listComments(rsrc));
   }
 
-  private Iterable<RobotComment> listComments(RevisionResource rsrc) throws OrmException {
+  private Iterable<RobotComment> listComments(RevisionResource rsrc) {
     return commentsUtil.robotCommentsByPatchSet(rsrc.getNotes(), rsrc.getPatchSet().getId());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/MarkAsReviewed.java b/java/com/google/gerrit/server/restapi/change/MarkAsReviewed.java
index 49c8fb6..4c942d2 100644
--- a/java/com/google/gerrit/server/restapi/change/MarkAsReviewed.java
+++ b/java/com/google/gerrit/server/restapi/change/MarkAsReviewed.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestApiException;
@@ -24,7 +25,6 @@
 import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -52,7 +52,7 @@
 
   @Override
   public Response<String> apply(ChangeResource rsrc, Input input)
-      throws RestApiException, OrmException, IllegalLabelException {
+      throws RestApiException, IllegalLabelException {
     stars.markAsReviewed(rsrc);
     return Response.ok("");
   }
@@ -62,7 +62,7 @@
       return changeDataFactory
           .create(rsrc.getNotes())
           .isReviewedBy(rsrc.getUser().asIdentifiedUser().getAccountId());
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("failed to check if change is reviewed");
     }
     return false;
diff --git a/java/com/google/gerrit/server/restapi/change/MarkAsUnreviewed.java b/java/com/google/gerrit/server/restapi/change/MarkAsUnreviewed.java
index 0651e7b..5945b14 100644
--- a/java/com/google/gerrit/server/restapi/change/MarkAsUnreviewed.java
+++ b/java/com/google/gerrit/server/restapi/change/MarkAsUnreviewed.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
@@ -23,7 +24,6 @@
 import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -50,8 +50,7 @@
   }
 
   @Override
-  public Response<String> apply(ChangeResource rsrc, Input input)
-      throws OrmException, IllegalLabelException {
+  public Response<String> apply(ChangeResource rsrc, Input input) throws IllegalLabelException {
     stars.markAsUnreviewed(rsrc);
     return Response.ok("");
   }
@@ -61,7 +60,7 @@
       return changeDataFactory
           .create(rsrc.getNotes())
           .isReviewedBy(rsrc.getUser().asIdentifiedUser().getAccountId());
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("failed to check if change is reviewed");
     }
     return false;
diff --git a/java/com/google/gerrit/server/restapi/change/Mergeable.java b/java/com/google/gerrit/server/restapi/change/Mergeable.java
index 4441397..ece8938 100644
--- a/java/com/google/gerrit/server/restapi/change/Mergeable.java
+++ b/java/com/google/gerrit/server/restapi/change/Mergeable.java
@@ -16,6 +16,7 @@
 
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.extensions.common.MergeableInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -36,7 +37,6 @@
 import com.google.gerrit.server.project.SubmitRuleEvaluator;
 import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -90,8 +90,7 @@
 
   @Override
   public MergeableInfo apply(RevisionResource resource)
-      throws AuthException, ResourceConflictException, BadRequestException, OrmException,
-          IOException {
+      throws AuthException, ResourceConflictException, BadRequestException, IOException {
     Change change = resource.getChange();
     PatchSet ps = resource.getPatchSet();
     MergeableInfo result = new MergeableInfo();
@@ -136,10 +135,10 @@
     return result;
   }
 
-  private SubmitType getSubmitType(ChangeData cd) throws OrmException {
+  private SubmitType getSubmitType(ChangeData cd) {
     SubmitTypeRecord rec = submitRuleEvaluator.getSubmitType(cd);
     if (rec.status != SubmitTypeRecord.Status.OK) {
-      throw new OrmException("Submit type rule failed: " + rec);
+      throw new StorageException("Submit type rule failed: " + rec);
     }
     return rec.type;
   }
diff --git a/java/com/google/gerrit/server/restapi/change/Move.java b/java/com/google/gerrit/server/restapi/change/Move.java
index 36f91bb..f2335b1 100644
--- a/java/com/google/gerrit/server/restapi/change/Move.java
+++ b/java/com/google/gerrit/server/restapi/change/Move.java
@@ -23,6 +23,7 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.MoveInput;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -61,7 +62,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -116,8 +116,7 @@
   @Override
   protected ChangeInfo applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, MoveInput input)
-      throws RestApiException, OrmException, UpdateException, PermissionBackendException,
-          IOException {
+      throws RestApiException, UpdateException, PermissionBackendException, IOException {
     if (!moveEnabled) {
       // This will be removed with the above config once we reach consensus for the move change
       // behavior. See: https://bugs.chromium.org/p/gerrit/issues/detail?id=9877
@@ -177,8 +176,7 @@
     }
 
     @Override
-    public boolean updateChange(ChangeContext ctx)
-        throws OrmException, ResourceConflictException, IOException {
+    public boolean updateChange(ChangeContext ctx) throws ResourceConflictException, IOException {
       change = ctx.getChange();
       if (!change.isNew()) {
         throw new ResourceConflictException("Change is " + ChangeUtil.status(change));
@@ -258,7 +256,7 @@
      */
     private void updateApprovals(
         ChangeContext ctx, ChangeUpdate update, PatchSet.Id psId, Project.NameKey project)
-        throws IOException, OrmException {
+        throws IOException {
       List<PatchSetApproval> approvals = new ArrayList<>();
       for (PatchSetApproval psa :
           approvalsUtil.byPatchSet(
@@ -310,7 +308,7 @@
       if (psUtil.isPatchSetLocked(rsrc.getNotes())) {
         return description;
       }
-    } catch (OrmException | IOException e) {
+    } catch (StorageException | IOException e) {
       logger.atSevere().withCause(e).log(
           "Failed to check if the current patch set of change %s is locked", change.getId());
       return description;
diff --git a/java/com/google/gerrit/server/restapi/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
index 7658f34..9a843cd 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -119,7 +119,6 @@
 import com.google.gerrit.server.util.LabelVote;
 import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.gson.Gson;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -220,15 +219,15 @@
   @Override
   protected Response<ReviewResult> applyImpl(
       BatchUpdate.Factory updateFactory, RevisionResource revision, ReviewInput input)
-      throws RestApiException, UpdateException, OrmException, IOException,
-          PermissionBackendException, ConfigInvalidException, PatchListNotAvailableException {
+      throws RestApiException, UpdateException, IOException, PermissionBackendException,
+          ConfigInvalidException, PatchListNotAvailableException {
     return apply(updateFactory, revision, input, TimeUtil.nowTs());
   }
 
   public Response<ReviewResult> apply(
       BatchUpdate.Factory updateFactory, RevisionResource revision, ReviewInput input, Timestamp ts)
-      throws RestApiException, UpdateException, OrmException, IOException,
-          PermissionBackendException, ConfigInvalidException, PatchListNotAvailableException {
+      throws RestApiException, UpdateException, IOException, PermissionBackendException,
+          ConfigInvalidException, PatchListNotAvailableException {
     // Respect timestamp, but truncate at change created-on time.
     ts = Ordering.natural().max(ts, revision.getChange().getCreatedOn());
     if (revision.getEdit().isPresent()) {
@@ -438,7 +437,7 @@
   }
 
   private RevisionResource onBehalfOf(RevisionResource rev, LabelTypes labelTypes, ReviewInput in)
-      throws BadRequestException, AuthException, UnprocessableEntityException, OrmException,
+      throws BadRequestException, AuthException, UnprocessableEntityException,
           PermissionBackendException, IOException, ConfigInvalidException {
     if (in.labels == null || in.labels.isEmpty()) {
       throw new AuthException(
@@ -854,7 +853,7 @@
 
     @Override
     public boolean updateChange(ChangeContext ctx)
-        throws OrmException, ResourceConflictException, UnprocessableEntityException, IOException,
+        throws ResourceConflictException, UnprocessableEntityException, IOException,
             PatchListNotAvailableException {
       user = ctx.getIdentifiedUser();
       notes = ctx.getNotes();
@@ -868,7 +867,7 @@
     }
 
     @Override
-    public void postUpdate(Context ctx) throws OrmException {
+    public void postUpdate(Context ctx) {
       if (message == null) {
         return;
       }
@@ -889,7 +888,7 @@
     }
 
     private boolean insertComments(ChangeContext ctx)
-        throws OrmException, UnprocessableEntityException, PatchListNotAvailableException {
+        throws UnprocessableEntityException, PatchListNotAvailableException {
       Map<String, List<CommentInput>> map = in.comments;
       if (map == null) {
         map = Collections.emptyMap();
@@ -949,8 +948,7 @@
       return !toPublish.isEmpty();
     }
 
-    private boolean insertRobotComments(ChangeContext ctx)
-        throws OrmException, PatchListNotAvailableException {
+    private boolean insertRobotComments(ChangeContext ctx) throws PatchListNotAvailableException {
       if (in.robotComments == null) {
         return false;
       }
@@ -962,7 +960,7 @@
     }
 
     private List<RobotComment> getNewRobotComments(ChangeContext ctx)
-        throws OrmException, PatchListNotAvailableException {
+        throws PatchListNotAvailableException {
       List<RobotComment> toAdd = new ArrayList<>(in.robotComments.size());
 
       Set<CommentSetEntry> existingIds =
@@ -1031,19 +1029,19 @@
       return new FixReplacement(fixReplacementInfo.path, range, fixReplacementInfo.replacement);
     }
 
-    private Set<CommentSetEntry> readExistingComments(ChangeContext ctx) throws OrmException {
+    private Set<CommentSetEntry> readExistingComments(ChangeContext ctx) {
       return commentsUtil.publishedByChange(ctx.getNotes()).stream()
           .map(CommentSetEntry::create)
           .collect(toSet());
     }
 
-    private Set<CommentSetEntry> readExistingRobotComments(ChangeContext ctx) throws OrmException {
+    private Set<CommentSetEntry> readExistingRobotComments(ChangeContext ctx) {
       return commentsUtil.robotCommentsByChange(ctx.getNotes()).stream()
           .map(CommentSetEntry::create)
           .collect(toSet());
     }
 
-    private Map<String, Comment> changeDrafts(ChangeContext ctx) throws OrmException {
+    private Map<String, Comment> changeDrafts(ChangeContext ctx) {
       Map<String, Comment> drafts = new HashMap<>();
       for (Comment c : commentsUtil.draftByChangeAuthor(ctx.getNotes(), user.getAccountId())) {
         c.tag = in.tag;
@@ -1052,7 +1050,7 @@
       return drafts;
     }
 
-    private Map<String, Comment> patchSetDrafts(ChangeContext ctx) throws OrmException {
+    private Map<String, Comment> patchSetDrafts(ChangeContext ctx) {
       Map<String, Comment> drafts = new HashMap<>();
       for (Comment c :
           commentsUtil.draftByPatchSetAuthor(psId, user.getAccountId(), ctx.getNotes())) {
@@ -1100,7 +1098,7 @@
       return previous;
     }
 
-    private boolean isReviewer(ChangeContext ctx) throws OrmException {
+    private boolean isReviewer(ChangeContext ctx) {
       if (ctx.getAccountId().equals(ctx.getChange().getOwner())) {
         return true;
       }
@@ -1113,7 +1111,7 @@
     }
 
     private boolean updateLabels(ProjectState projectState, ChangeContext ctx)
-        throws OrmException, ResourceConflictException, IOException {
+        throws ResourceConflictException, IOException {
       Map<String, Short> inLabels = firstNonNull(in.labels, Collections.emptyMap());
 
       // If no labels were modified and change is closed, abort early.
@@ -1297,7 +1295,7 @@
 
     private Map<String, PatchSetApproval> scanLabels(
         ProjectState projectState, ChangeContext ctx, List<PatchSetApproval> del)
-        throws OrmException, IOException {
+        throws IOException {
       LabelTypes labelTypes = projectState.getLabelTypes(ctx.getNotes());
       Map<String, PatchSetApproval> current = new HashMap<>();
 
diff --git a/java/com/google/gerrit/server/restapi/change/PostReviewers.java b/java/com/google/gerrit/server/restapi/change/PostReviewers.java
index 4aeb07f..8abd964 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReviewers.java
@@ -32,7 +32,6 @@
 import com.google.gerrit.server.update.RetryingRestCollectionModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -62,8 +61,8 @@
   @Override
   protected AddReviewerResult applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, AddReviewerInput input)
-      throws IOException, OrmException, RestApiException, UpdateException,
-          PermissionBackendException, ConfigInvalidException {
+      throws IOException, RestApiException, UpdateException, PermissionBackendException,
+          ConfigInvalidException {
     if (input.reviewer == null) {
       throw new BadRequestException("missing reviewer field");
     }
@@ -86,7 +85,7 @@
   }
 
   private NotifyResolver.Result resolveNotify(ChangeResource rsrc, AddReviewerInput input)
-      throws BadRequestException, OrmException, ConfigInvalidException, IOException {
+      throws BadRequestException, ConfigInvalidException, IOException {
     NotifyHandling notifyHandling = input.notify;
     if (notifyHandling == null) {
       notifyHandling =
diff --git a/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java b/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
index 08d86da..7137b6e 100644
--- a/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
+++ b/java/com/google/gerrit/server/restapi/change/PreviewSubmit.java
@@ -39,7 +39,6 @@
 import com.google.gerrit.server.submit.MergeOpRepoManager;
 import com.google.gerrit.server.submit.MergeOpRepoManager.OpenRepo;
 import com.google.gerrit.server.update.UpdateException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -82,7 +81,7 @@
 
   @Override
   public BinaryResult apply(RevisionResource rsrc)
-      throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException,
+      throws RestApiException, UpdateException, IOException, ConfigInvalidException,
           PermissionBackendException {
     if (Strings.isNullOrEmpty(format)) {
       throw new BadRequestException("format is not specified");
@@ -110,7 +109,7 @@
   }
 
   private BinaryResult getBundles(RevisionResource rsrc, ArchiveFormat f)
-      throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException,
+      throws RestApiException, UpdateException, IOException, ConfigInvalidException,
           PermissionBackendException {
     IdentifiedUser caller = rsrc.getUser().asIdentifiedUser();
     Change change = rsrc.getChange();
@@ -124,8 +123,7 @@
           .setContentType(f.getMimeType())
           .setAttachmentName("submit-preview-" + change.getChangeId() + "." + format);
       return bin;
-    } catch (OrmException
-        | RestApiException
+    } catch (RestApiException
         | UpdateException
         | IOException
         | ConfigInvalidException
diff --git a/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java b/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
index b0cad84..a47037c 100644
--- a/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/PublishChangeEdit.java
@@ -31,7 +31,6 @@
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -60,7 +59,7 @@
   @Override
   protected Response<?> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, PublishChangeEditInput in)
-      throws IOException, OrmException, RestApiException, UpdateException, ConfigInvalidException,
+      throws IOException, RestApiException, UpdateException, ConfigInvalidException,
           NoSuchProjectException {
     contributorAgreementsChecker.check(rsrc.getProject(), rsrc.getUser());
     Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getNotes(), rsrc.getUser());
diff --git a/java/com/google/gerrit/server/restapi/change/PutAssignee.java b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
index a7dcc12..de62725 100644
--- a/java/com/google/gerrit/server/restapi/change/PutAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/PutAssignee.java
@@ -39,7 +39,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -74,8 +73,8 @@
   @Override
   protected AccountInfo applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, AssigneeInput input)
-      throws RestApiException, UpdateException, OrmException, IOException,
-          PermissionBackendException, ConfigInvalidException {
+      throws RestApiException, UpdateException, IOException, PermissionBackendException,
+          ConfigInvalidException {
     rsrc.permissions().check(ChangePermission.EDIT_ASSIGNEE);
 
     input.assignee = Strings.nullToEmpty(input.assignee).trim();
@@ -108,7 +107,7 @@
   }
 
   private ReviewerAddition addAssigneeAsCC(ChangeResource rsrc, String assignee)
-      throws OrmException, IOException, PermissionBackendException, ConfigInvalidException {
+      throws IOException, PermissionBackendException, ConfigInvalidException {
     AddReviewerInput reviewerInput = new AddReviewerInput();
     reviewerInput.reviewer = assignee;
     reviewerInput.state = ReviewerState.CC;
diff --git a/java/com/google/gerrit/server/restapi/change/PutDescription.java b/java/com/google/gerrit/server/restapi/change/PutDescription.java
index b5c6c95..0ec38e1 100644
--- a/java/com/google/gerrit/server/restapi/change/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDescription.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -82,7 +81,7 @@
     }
 
     @Override
-    public boolean updateChange(ChangeContext ctx) throws OrmException {
+    public boolean updateChange(ChangeContext ctx) {
       ChangeUpdate update = ctx.getUpdate(psId);
       newDescription = Strings.nullToEmpty(input.description);
       oldDescription = Strings.nullToEmpty(psUtil.get(ctx.getNotes(), psId).getDescription());
diff --git a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
index 974d715..f6c9abe 100644
--- a/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
+++ b/java/com/google/gerrit/server/restapi/change/PutDraftComment.java
@@ -40,7 +40,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -77,7 +76,7 @@
   @Override
   protected Response<CommentInfo> applyImpl(
       BatchUpdate.Factory updateFactory, DraftCommentResource rsrc, DraftInput in)
-      throws RestApiException, UpdateException, OrmException, PermissionBackendException {
+      throws RestApiException, UpdateException, PermissionBackendException {
     if (in == null || in.message == null || in.message.trim().isEmpty()) {
       return delete.applyImpl(updateFactory, rsrc, null);
     } else if (in.id != null && !rsrc.getId().equals(in.id)) {
@@ -111,7 +110,7 @@
 
     @Override
     public boolean updateChange(ChangeContext ctx)
-        throws ResourceNotFoundException, OrmException, PatchListNotAvailableException {
+        throws ResourceNotFoundException, PatchListNotAvailableException {
       Optional<Comment> maybeComment =
           commentsUtil.getDraft(ctx.getNotes(), ctx.getIdentifiedUser(), key);
       if (!maybeComment.isPresent()) {
diff --git a/java/com/google/gerrit/server/restapi/change/PutMessage.java b/java/com/google/gerrit/server/restapi/change/PutMessage.java
index 39dfc7f..c542164 100644
--- a/java/com/google/gerrit/server/restapi/change/PutMessage.java
+++ b/java/com/google/gerrit/server/restapi/change/PutMessage.java
@@ -43,7 +43,6 @@
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.CommitMessageUtil;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -100,7 +99,7 @@
   protected Response<String> applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource resource, CommitMessageInput input)
       throws IOException, RestApiException, UpdateException, PermissionBackendException,
-          OrmException, ConfigInvalidException {
+          ConfigInvalidException {
     PatchSet ps = psUtil.current(resource.getNotes());
     if (ps == null) {
       throw new ResourceConflictException("current revision is missing");
@@ -149,7 +148,7 @@
   }
 
   private NotifyResolver.Result resolveNotify(CommitMessageInput input, ChangeResource resource)
-      throws BadRequestException, OrmException, ConfigInvalidException, IOException {
+      throws BadRequestException, ConfigInvalidException, IOException {
     NotifyHandling notifyHandling = input.notify;
     if (notifyHandling == null) {
       notifyHandling =
@@ -176,8 +175,7 @@
   }
 
   private void ensureCanEditCommitMessage(ChangeNotes changeNotes)
-      throws AuthException, PermissionBackendException, IOException, ResourceConflictException,
-          OrmException {
+      throws AuthException, PermissionBackendException, IOException, ResourceConflictException {
     if (!userProvider.get().isIdentifiedUser()) {
       throw new AuthException("Authentication required");
     }
diff --git a/java/com/google/gerrit/server/restapi/change/PutTopic.java b/java/com/google/gerrit/server/restapi/change/PutTopic.java
index bc272e9..abfa49b 100644
--- a/java/com/google/gerrit/server/restapi/change/PutTopic.java
+++ b/java/com/google/gerrit/server/restapi/change/PutTopic.java
@@ -37,7 +37,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -93,7 +92,7 @@
     }
 
     @Override
-    public boolean updateChange(ChangeContext ctx) throws OrmException {
+    public boolean updateChange(ChangeContext ctx) {
       change = ctx.getChange();
       ChangeUpdate update = ctx.getUpdate(change.currentPatchSetId());
       newTopicName = Strings.nullToEmpty(input.topic);
diff --git a/java/com/google/gerrit/server/restapi/change/QueryChanges.java b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
index 6176ea0..5ee49ff 100644
--- a/java/com/google/gerrit/server/restapi/change/QueryChanges.java
+++ b/java/com/google/gerrit/server/restapi/change/QueryChanges.java
@@ -32,7 +32,6 @@
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeQueryBuilder;
 import com.google.gerrit.server.query.change.ChangeQueryProcessor;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -115,7 +114,7 @@
 
   @Override
   public List<?> apply(TopLevelResource rsrc)
-      throws BadRequestException, AuthException, OrmException, PermissionBackendException {
+      throws BadRequestException, AuthException, PermissionBackendException {
     List<List<ChangeInfo>> out;
     try {
       out = query();
@@ -128,8 +127,7 @@
     return out.size() == 1 ? out.get(0) : out;
   }
 
-  private List<List<ChangeInfo>> query()
-      throws OrmException, QueryParseException, PermissionBackendException {
+  private List<List<ChangeInfo>> query() throws QueryParseException, PermissionBackendException {
     if (imp.isDisabled()) {
       throw new QueryParseException("query disabled");
     }
diff --git a/java/com/google/gerrit/server/restapi/change/Rebase.java b/java/com/google/gerrit/server/restapi/change/Rebase.java
index afb674b..4e6f65e 100644
--- a/java/com/google/gerrit/server/restapi/change/Rebase.java
+++ b/java/com/google/gerrit/server/restapi/change/Rebase.java
@@ -17,6 +17,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.RebaseInput;
 import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -48,7 +49,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -99,8 +99,7 @@
   @Override
   protected ChangeInfo applyImpl(
       BatchUpdate.Factory updateFactory, RevisionResource rsrc, RebaseInput input)
-      throws OrmException, UpdateException, RestApiException, IOException,
-          PermissionBackendException {
+      throws UpdateException, RestApiException, IOException, PermissionBackendException {
     // Not allowed to rebase if the current patch set is locked.
     patchSetUtil.checkPatchSetNotLocked(rsrc.getNotes());
 
@@ -136,7 +135,7 @@
 
   private ObjectId findBaseRev(
       Repository repo, RevWalk rw, RevisionResource rsrc, RebaseInput input)
-      throws RestApiException, OrmException, IOException, NoSuchChangeException, AuthException,
+      throws RestApiException, IOException, NoSuchChangeException, AuthException,
           PermissionBackendException {
     Branch.NameKey destRefKey = rsrc.getChange().getDest();
     if (input == null || input.base == null) {
@@ -224,7 +223,7 @@
       if (patchSetUtil.isPatchSetLocked(rsrc.getNotes())) {
         return description;
       }
-    } catch (OrmException | IOException e) {
+    } catch (StorageException | IOException e) {
       logger.atSevere().withCause(e).log(
           "Failed to check if the current patch set of change %s is locked", change.getId());
       return description;
@@ -267,8 +266,7 @@
     @Override
     protected ChangeInfo applyImpl(
         BatchUpdate.Factory updateFactory, ChangeResource rsrc, RebaseInput input)
-        throws OrmException, UpdateException, RestApiException, IOException,
-            PermissionBackendException {
+        throws UpdateException, RestApiException, IOException, PermissionBackendException {
       PatchSet ps = psUtil.current(rsrc.getNotes());
       if (ps == null) {
         throw new ResourceConflictException("current revision is missing");
diff --git a/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java b/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
index 6020e95..81294ed 100644
--- a/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
+++ b/java/com/google/gerrit/server/restapi/change/RebaseChangeEdit.java
@@ -27,7 +27,6 @@
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.RetryHelper;
 import com.google.gerrit.server.update.RetryingRestModifyView;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -50,8 +49,7 @@
 
   @Override
   protected Response<?> applyImpl(BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input in)
-      throws AuthException, ResourceConflictException, IOException, OrmException,
-          PermissionBackendException {
+      throws AuthException, ResourceConflictException, IOException, PermissionBackendException {
     Project.NameKey project = rsrc.getProject();
     try (Repository repository = repositoryManager.openRepository(project)) {
       editModifier.rebaseEdit(repository, rsrc.getNotes());
diff --git a/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java b/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
index 8634e1d..937ab56 100644
--- a/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
+++ b/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
@@ -35,7 +35,6 @@
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -72,7 +71,7 @@
   }
 
   public List<PatchSetData> sort(List<ChangeData> in, PatchSet startPs)
-      throws OrmException, IOException, PermissionBackendException {
+      throws IOException, PermissionBackendException {
     checkArgument(!in.isEmpty(), "Input may not be empty");
     // Map of all patch sets, keyed by commit SHA-1.
     Map<String, PatchSetData> byId = collectById(in);
@@ -113,8 +112,7 @@
     return result;
   }
 
-  private Map<String, PatchSetData> collectById(List<ChangeData> in)
-      throws OrmException, IOException {
+  private Map<String, PatchSetData> collectById(List<ChangeData> in) throws IOException {
     Project.NameKey project = in.get(0).change().getProject();
     Map<String, PatchSetData> result = Maps.newHashMapWithExpectedSize(in.size() * 3);
     try (Repository repo = repoManager.openRepository(project);
diff --git a/java/com/google/gerrit/server/restapi/change/Restore.java b/java/com/google/gerrit/server/restapi/change/Restore.java
index 40ef867..5f56cdb 100644
--- a/java/com/google/gerrit/server/restapi/change/Restore.java
+++ b/java/com/google/gerrit/server/restapi/change/Restore.java
@@ -16,6 +16,7 @@
 
 import com.google.common.base.Strings;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.RestoreInput;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -45,7 +46,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -83,8 +83,7 @@
   @Override
   protected ChangeInfo applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, RestoreInput input)
-      throws RestApiException, UpdateException, OrmException, PermissionBackendException,
-          IOException {
+      throws RestApiException, UpdateException, PermissionBackendException, IOException {
     // Not allowed to restore if the current patch set is locked.
     psUtil.checkPatchSetNotLocked(rsrc.getNotes());
 
@@ -111,7 +110,7 @@
     }
 
     @Override
-    public boolean updateChange(ChangeContext ctx) throws OrmException, ResourceConflictException {
+    public boolean updateChange(ChangeContext ctx) throws ResourceConflictException {
       change = ctx.getChange();
       if (change == null || !change.isAbandoned()) {
         throw new ResourceConflictException("change is " + ChangeUtil.status(change));
@@ -139,7 +138,7 @@
     }
 
     @Override
-    public void postUpdate(Context ctx) throws OrmException {
+    public void postUpdate(Context ctx) {
       try {
         ReplyToChangeSender cm = restoredSenderFactory.create(ctx.getProject(), change.getId());
         cm.setFrom(ctx.getAccountId());
@@ -180,7 +179,7 @@
       if (psUtil.isPatchSetLocked(rsrc.getNotes())) {
         return description;
       }
-    } catch (OrmException | IOException e) {
+    } catch (StorageException | IOException e) {
       logger.atSevere().withCause(e).log(
           "Failed to check if the current patch set of change %s is locked", change.getId());
       return description;
diff --git a/java/com/google/gerrit/server/restapi/change/Revert.java b/java/com/google/gerrit/server/restapi/change/Revert.java
index 9937160..3fe6770 100644
--- a/java/com/google/gerrit/server/restapi/change/Revert.java
+++ b/java/com/google/gerrit/server/restapi/change/Revert.java
@@ -64,7 +64,6 @@
 import com.google.gerrit.server.update.RetryingRestModifyView;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -142,7 +141,7 @@
   @Override
   public ChangeInfo applyImpl(
       BatchUpdate.Factory updateFactory, ChangeResource rsrc, RevertInput input)
-      throws IOException, OrmException, RestApiException, UpdateException, NoSuchChangeException,
+      throws IOException, RestApiException, UpdateException, NoSuchChangeException,
           PermissionBackendException, NoSuchProjectException, ConfigInvalidException {
     Change change = rsrc.getChange();
     if (!change.isMerged()) {
@@ -159,7 +158,7 @@
 
   private Change.Id revert(
       BatchUpdate.Factory updateFactory, ChangeNotes notes, CurrentUser user, RevertInput input)
-      throws OrmException, IOException, RestApiException, UpdateException, ConfigInvalidException {
+      throws IOException, RestApiException, UpdateException, ConfigInvalidException {
     String message = Strings.emptyToNull(input.message);
     Change.Id changeIdToRevert = notes.getChangeId();
     PatchSet.Id patchSetId = notes.getChange().currentPatchSetId();
diff --git a/java/com/google/gerrit/server/restapi/change/Reviewed.java b/java/com/google/gerrit/server/restapi/change/Reviewed.java
index accc355..113ed82 100644
--- a/java/com/google/gerrit/server/restapi/change/Reviewed.java
+++ b/java/com/google/gerrit/server/restapi/change/Reviewed.java
@@ -14,13 +14,13 @@
 
 package com.google.gerrit.server.restapi.change;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.change.AccountPatchReviewStore;
 import com.google.gerrit.server.change.FileResource;
 import com.google.gerrit.server.plugincontext.PluginItemContext;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -36,7 +36,7 @@
     }
 
     @Override
-    public Response<String> apply(FileResource resource, Input input) throws OrmException {
+    public Response<String> apply(FileResource resource, Input input) {
       boolean reviewFlagUpdated =
           accountPatchReviewStore.call(
               s ->
@@ -44,7 +44,7 @@
                       resource.getPatchKey().getParentKey(),
                       resource.getAccountId(),
                       resource.getPatchKey().getFileName()),
-              OrmException.class);
+              StorageException.class);
       return reviewFlagUpdated ? Response.created("") : Response.ok("");
     }
   }
@@ -59,14 +59,14 @@
     }
 
     @Override
-    public Response<?> apply(FileResource resource, Input input) throws OrmException {
+    public Response<?> apply(FileResource resource, Input input) {
       accountPatchReviewStore.run(
           s ->
               s.clearReviewed(
                   resource.getPatchKey().getParentKey(),
                   resource.getAccountId(),
                   resource.getPatchKey().getFileName()),
-          OrmException.class);
+          StorageException.class);
       return Response.none();
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
index 1d4d4dd..ed487f7 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
@@ -39,7 +39,6 @@
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeQueryBuilder;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -101,7 +100,7 @@
       SuggestReviewers suggestReviewers,
       ProjectState projectState,
       List<Account.Id> candidateList)
-      throws OrmException, IOException, ConfigInvalidException {
+      throws IOException, ConfigInvalidException {
     logger.atFine().log("Candidates %s", candidateList);
 
     String query = suggestReviewers.getQuery();
@@ -198,7 +197,7 @@
   }
 
   private Map<Account.Id, MutableDouble> baseRankingForEmptyQuery(double baseWeight)
-      throws OrmException, IOException, ConfigInvalidException {
+      throws IOException, ConfigInvalidException {
     // Get the user's last 25 changes, check approvals
     try {
       List<ChangeData> result =
@@ -228,7 +227,7 @@
 
   private Map<Account.Id, MutableDouble> baseRankingForCandidateList(
       List<Account.Id> candidates, ProjectState projectState, double baseWeight)
-      throws OrmException, IOException, ConfigInvalidException {
+      throws IOException, ConfigInvalidException {
     // Get each reviewer's activity based on number of applied labels
     // (weighted 10d), number of comments (weighted 0.5d) and number of owned
     // changes (weighted 1d).
diff --git a/java/com/google/gerrit/server/restapi/change/Reviewers.java b/java/com/google/gerrit/server/restapi/change/Reviewers.java
index cf69080..546ca01 100644
--- a/java/com/google/gerrit/server/restapi/change/Reviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/Reviewers.java
@@ -27,7 +27,6 @@
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.restapi.account.AccountsCollection;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -68,8 +67,7 @@
 
   @Override
   public ReviewerResource parse(ChangeResource rsrc, IdString id)
-      throws OrmException, ResourceNotFoundException, AuthException, IOException,
-          ConfigInvalidException {
+      throws ResourceNotFoundException, AuthException, IOException, ConfigInvalidException {
     Address address = Address.tryParse(id.get());
 
     Account.Id accountId = null;
@@ -93,7 +91,7 @@
     throw new ResourceNotFoundException(id);
   }
 
-  private Collection<Account.Id> fetchAccountIds(ChangeResource rsrc) throws OrmException {
+  private Collection<Account.Id> fetchAccountIds(ChangeResource rsrc) {
     return approvalsUtil.getReviewers(rsrc.getNotes()).all();
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
index 343afc3..779f277 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
@@ -57,7 +57,6 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.account.AccountPredicates;
 import com.google.gerrit.server.query.account.AccountQueryBuilder;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -156,7 +155,7 @@
   }
 
   public interface VisibilityControl {
-    boolean isVisibleTo(Account.Id account) throws OrmException;
+    boolean isVisibleTo(Account.Id account);
   }
 
   public List<SuggestedReviewerInfo> suggestReviewers(
@@ -165,7 +164,7 @@
       ProjectState projectState,
       VisibilityControl visibilityControl,
       boolean excludeGroups)
-      throws IOException, OrmException, ConfigInvalidException, PermissionBackendException {
+      throws IOException, ConfigInvalidException, PermissionBackendException {
     CurrentUser currentUser = self.get();
     if (changeNotes != null) {
       logger.atFine().log(
@@ -224,7 +223,7 @@
     return suggestedReviewers;
   }
 
-  private List<Account.Id> suggestAccounts(SuggestReviewers suggestReviewers) throws OrmException {
+  private List<Account.Id> suggestAccounts(SuggestReviewers suggestReviewers) {
     try (Timer0.Context ctx = metrics.queryAccountsLatency.start()) {
       try {
         // For performance reasons we don't use AccountQueryProvider as it would always load the
@@ -264,7 +263,7 @@
       VisibilityControl visibilityControl,
       boolean excludeGroups,
       List<Account.Id> filteredRecommendations)
-      throws OrmException, PermissionBackendException, IOException {
+      throws PermissionBackendException, IOException {
     List<SuggestedReviewerInfo> suggestedReviewers = loadAccounts(filteredRecommendations);
 
     int limit = suggestReviewers.getLimit();
@@ -293,7 +292,7 @@
       SuggestReviewers suggestReviewers,
       ProjectState projectState,
       List<Account.Id> candidateList)
-      throws OrmException, IOException, ConfigInvalidException {
+      throws IOException, ConfigInvalidException {
     try (Timer0.Context ctx = metrics.recommendAccountsLatency.start()) {
       return reviewerRecommender.suggestReviewers(
           changeNotes, suggestReviewers, projectState, candidateList);
@@ -329,7 +328,7 @@
       ProjectState projectState,
       VisibilityControl visibilityControl,
       int limit)
-      throws OrmException, IOException {
+      throws IOException {
     try (Timer0.Context ctx = metrics.queryGroupsLatency.start()) {
       List<SuggestedReviewerInfo> groups = new ArrayList<>();
       for (GroupReference g : suggestAccountGroups(suggestReviewers, projectState)) {
@@ -375,7 +374,7 @@
       Project project,
       GroupReference group,
       VisibilityControl visibilityControl)
-      throws OrmException, IOException {
+      throws IOException {
     GroupAsReviewer result = new GroupAsReviewer();
     int maxAllowed = suggestReviewers.getMaxAllowed();
     int maxAllowedWithoutConfirmation = suggestReviewers.getMaxAllowedWithoutConfirmation();
diff --git a/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java b/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
index 60c9a54..a41143c 100644
--- a/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/RevisionReviewers.java
@@ -28,7 +28,6 @@
 import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.restapi.account.AccountsCollection;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -69,8 +68,8 @@
 
   @Override
   public ReviewerResource parse(RevisionResource rsrc, IdString id)
-      throws OrmException, ResourceNotFoundException, AuthException, MethodNotAllowedException,
-          IOException, ConfigInvalidException {
+      throws ResourceNotFoundException, AuthException, MethodNotAllowedException, IOException,
+          ConfigInvalidException {
     if (!rsrc.isCurrent()) {
       throw new MethodNotAllowedException("Cannot access on non-current patch set");
     }
diff --git a/java/com/google/gerrit/server/restapi/change/Revisions.java b/java/com/google/gerrit/server/restapi/change/Revisions.java
index 84257e9..c5cce4f 100644
--- a/java/com/google/gerrit/server/restapi/change/Revisions.java
+++ b/java/com/google/gerrit/server/restapi/change/Revisions.java
@@ -33,7 +33,6 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -77,8 +76,7 @@
 
   @Override
   public RevisionResource parse(ChangeResource change, IdString id)
-      throws ResourceNotFoundException, AuthException, OrmException, IOException,
-          PermissionBackendException {
+      throws ResourceNotFoundException, AuthException, IOException, PermissionBackendException {
     if (id.get().equals("current")) {
       PatchSet ps = psUtil.current(change.getNotes());
       if (ps != null && visible(change)) {
@@ -117,7 +115,7 @@
   }
 
   private List<RevisionResource> find(ChangeResource change, String id)
-      throws OrmException, IOException, AuthException {
+      throws IOException, AuthException {
     if (id.equals("0") || id.equals("edit")) {
       return loadEdit(change, null);
     } else if (id.length() < 6 && id.matches("^[1-9][0-9]{0,4}$")) {
@@ -142,8 +140,7 @@
     }
   }
 
-  private List<RevisionResource> byLegacyPatchSetId(ChangeResource change, String id)
-      throws OrmException {
+  private List<RevisionResource> byLegacyPatchSetId(ChangeResource change, String id) {
     PatchSet ps =
         psUtil.get(change.getNotes(), new PatchSet.Id(change.getId(), Integer.parseInt(id)));
     if (ps != null) {
diff --git a/java/com/google/gerrit/server/restapi/change/RobotComments.java b/java/com/google/gerrit/server/restapi/change/RobotComments.java
index 6570ae0..1aa8c2a 100644
--- a/java/com/google/gerrit/server/restapi/change/RobotComments.java
+++ b/java/com/google/gerrit/server/restapi/change/RobotComments.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.change.RobotCommentResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -56,7 +55,7 @@
 
   @Override
   public RobotCommentResource parse(RevisionResource rev, IdString id)
-      throws ResourceNotFoundException, OrmException {
+      throws ResourceNotFoundException {
     String uuid = id.get();
     ChangeNotes notes = rev.getNotes();
 
diff --git a/java/com/google/gerrit/server/restapi/change/Submit.java b/java/com/google/gerrit/server/restapi/change/Submit.java
index fdfa799..d12d7c6 100644
--- a/java/com/google/gerrit/server/restapi/change/Submit.java
+++ b/java/com/google/gerrit/server/restapi/change/Submit.java
@@ -23,6 +23,7 @@
 import com.google.common.collect.Sets;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.ParameterizedString;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.SubmitInput;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -59,8 +60,6 @@
 import com.google.gerrit.server.submit.MergeOp;
 import com.google.gerrit.server.submit.MergeSuperSet;
 import com.google.gerrit.server.update.UpdateException;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.OrmRuntimeException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -69,7 +68,6 @@
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -176,8 +174,8 @@
 
   @Override
   public Output apply(RevisionResource rsrc, SubmitInput input)
-      throws RestApiException, RepositoryNotFoundException, IOException, OrmException,
-          PermissionBackendException, UpdateException, ConfigInvalidException {
+      throws RestApiException, RepositoryNotFoundException, IOException, PermissionBackendException,
+          UpdateException, ConfigInvalidException {
     input.onBehalfOf = Strings.emptyToNull(input.onBehalfOf);
     IdentifiedUser submitter;
     if (input.onBehalfOf != null) {
@@ -192,7 +190,7 @@
   }
 
   public Change mergeChange(RevisionResource rsrc, IdentifiedUser submitter, SubmitInput input)
-      throws OrmException, RestApiException, IOException, UpdateException, ConfigInvalidException,
+      throws RestApiException, IOException, UpdateException, ConfigInvalidException,
           PermissionBackendException {
     Change change = rsrc.getChange();
     if (!change.isNew()) {
@@ -209,11 +207,14 @@
 
     try (MergeOp op = mergeOpProvider.get()) {
       op.merge(change, submitter, true, input, false);
-      try {
-        change = changeNotesFactory.createChecked(change.getProject(), change.getId()).getChange();
-      } catch (NoSuchChangeException e) {
-        throw new ResourceConflictException("change is deleted");
-      }
+    }
+
+    // Read the ChangeNotes only after MergeOp is fully done (including MergeOp#close) to be sure
+    // to have the correct state of the repo.
+    try {
+      change = changeNotesFactory.createChecked(change.getProject(), change.getId()).getChange();
+    } catch (NoSuchChangeException e) {
+      throw new ResourceConflictException("change is deleted");
     }
 
     if (change.isMerged()) {
@@ -287,9 +288,9 @@
         return "Problems with change(s): "
             + unmergeable.stream().map(c -> c.getId().toString()).collect(joining(", "));
       }
-    } catch (PermissionBackendException | OrmException | IOException e) {
+    } catch (PermissionBackendException | IOException e) {
       logger.atSevere().withCause(e).log("Error checking if change is submittable");
-      throw new OrmRuntimeException("Could not determine problems for the change", e);
+      throw new StorageException("Could not determine problems for the change", e);
     }
     return null;
   }
@@ -310,7 +311,7 @@
       }
     } catch (IOException e) {
       logger.atSevere().withCause(e).log("Error checking if change is submittable");
-      throw new OrmRuntimeException("Could not determine problems for the change", e);
+      throw new StorageException("Could not determine problems for the change", e);
     }
 
     ChangeData cd = changeDataFactory.create(resource.getNotes());
@@ -318,42 +319,31 @@
       MergeOp.checkSubmitRule(cd, false);
     } catch (ResourceConflictException e) {
       return null; // submit not visible
-    } catch (OrmException e) {
-      logger.atSevere().withCause(e).log("Error checking if change is submittable");
-      throw new OrmRuntimeException("Could not determine problems for the change", e);
     }
 
     ChangeSet cs;
     try {
       cs = mergeSuperSet.get().completeChangeSet(cd.change(), resource.getUser());
-    } catch (OrmException | IOException | PermissionBackendException e) {
-      throw new OrmRuntimeException(
-          "Could not determine complete set of changes to be submitted", e);
+    } catch (IOException | PermissionBackendException e) {
+      throw new StorageException("Could not determine complete set of changes to be submitted", e);
     }
 
     String topic = change.getTopic();
     int topicSize = 0;
     if (!Strings.isNullOrEmpty(topic)) {
-      topicSize = getChangesByTopic(topic).size();
+      topicSize = queryProvider.get().noFields().byTopicOpen(topic).size();
     }
     boolean treatWithTopic = submitWholeTopic && !Strings.isNullOrEmpty(topic) && topicSize > 1;
 
     String submitProblems = problemsForSubmittingChangeset(cd, cs, resource.getUser());
 
-    Boolean enabled;
-    try {
-      // Recheck mergeability rather than using value stored in the index,
-      // which may be stale.
-      // TODO(dborowitz): This is ugly; consider providing a way to not read
-      // stored fields from the index in the first place.
-      // cd.setMergeable(null);
-      // That was done in unmergeableChanges which was called by
-      // problemsForSubmittingChangeset, so now it is safe to read from
-      // the cache, as it yields the same result.
-      enabled = cd.isMergeable();
-    } catch (OrmException e) {
-      throw new OrmRuntimeException("Could not determine mergeability", e);
-    }
+    // Recheck mergeability rather than using value stored in the index, which may be stale.
+    // TODO(dborowitz): This is ugly; consider providing a way to not read stored fields from the
+    // index in the first place.
+    // cd.setMergeable(null);
+    // That was done in unmergeableChanges which was called by problemsForSubmittingChangeset, so
+    // now it is safe to read from the cache, as it yields the same result.
+    Boolean enabled = cd.isMergeable();
 
     if (submitProblems != null) {
       return new UiAction.Description()
@@ -389,7 +379,7 @@
         .setEnabled(Boolean.TRUE.equals(enabled));
   }
 
-  public Collection<ChangeData> unmergeableChanges(ChangeSet cs) throws OrmException, IOException {
+  public Collection<ChangeData> unmergeableChanges(ChangeSet cs) throws IOException {
     Set<ChangeData> mergeabilityMap = new HashSet<>();
     for (ChangeData change : cs.changes()) {
       mergeabilityMap.add(change);
@@ -438,7 +428,7 @@
   }
 
   private HashMap<Change.Id, RevCommit> findCommits(
-      Collection<ChangeData> changes, Project.NameKey project) throws IOException, OrmException {
+      Collection<ChangeData> changes, Project.NameKey project) throws IOException {
     HashMap<Change.Id, RevCommit> commits = new HashMap<>();
     try (Repository repo = repoManager.openRepository(project);
         RevWalk walk = new RevWalk(repo)) {
@@ -453,8 +443,8 @@
   }
 
   private IdentifiedUser onBehalfOf(RevisionResource rsrc, SubmitInput in)
-      throws AuthException, UnprocessableEntityException, OrmException, PermissionBackendException,
-          IOException, ConfigInvalidException {
+      throws AuthException, UnprocessableEntityException, PermissionBackendException, IOException,
+          ConfigInvalidException {
     PermissionBackend.ForChange perm = rsrc.permissions();
     perm.check(ChangePermission.SUBMIT);
     perm.check(ChangePermission.SUBMIT_AS);
@@ -471,14 +461,6 @@
     return submitter;
   }
 
-  private List<ChangeData> getChangesByTopic(String topic) {
-    try {
-      return queryProvider.get().byTopicOpen(topic);
-    } catch (OrmException e) {
-      throw new OrmRuntimeException(e);
-    }
-  }
-
   public static class CurrentRevision implements RestModifyView<ChangeResource, SubmitInput> {
     private final Submit submit;
     private final ChangeJson.Factory json;
@@ -493,7 +475,7 @@
 
     @Override
     public ChangeInfo apply(ChangeResource rsrc, SubmitInput input)
-        throws RestApiException, RepositoryNotFoundException, IOException, OrmException,
+        throws RestApiException, RepositoryNotFoundException, IOException,
             PermissionBackendException, UpdateException, ConfigInvalidException {
       PatchSet ps = psUtil.current(rsrc.getNotes());
       if (ps == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java b/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
index 39413b6..efefc3e 100644
--- a/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
+++ b/java/com/google/gerrit/server/restapi/change/SubmittedTogether.java
@@ -19,6 +19,7 @@
 import static java.util.stream.Collectors.toList;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.SubmittedTogetherInfo;
 import com.google.gerrit.extensions.api.changes.SubmittedTogetherOption;
 import com.google.gerrit.extensions.client.ListChangesOption;
@@ -36,7 +37,6 @@
 import com.google.gerrit.server.query.change.InternalChangeQuery;
 import com.google.gerrit.server.submit.ChangeSet;
 import com.google.gerrit.server.submit.MergeSuperSet;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -108,7 +108,7 @@
   @Override
   public Object apply(ChangeResource resource)
       throws AuthException, BadRequestException, ResourceConflictException, IOException,
-          OrmException, PermissionBackendException {
+          PermissionBackendException {
     SubmittedTogetherInfo info = applyInfo(resource);
     if (options.isEmpty()) {
       return info.changes;
@@ -117,7 +117,7 @@
   }
 
   public SubmittedTogetherInfo applyInfo(ChangeResource resource)
-      throws AuthException, IOException, OrmException, PermissionBackendException {
+      throws AuthException, IOException, PermissionBackendException {
     Change c = resource.getChange();
     try {
       List<ChangeData> cds;
@@ -144,13 +144,13 @@
       info.changes = json.create(jsonOpt).format(cds);
       info.nonVisibleChanges = hidden;
       return info;
-    } catch (OrmException | IOException e) {
+    } catch (StorageException | IOException e) {
       logger.atSevere().withCause(e).log("Error on getting a ChangeSet");
       throw e;
     }
   }
 
-  private List<ChangeData> sort(List<ChangeData> cds, int hidden) throws OrmException, IOException {
+  private List<ChangeData> sort(List<ChangeData> cds, int hidden) throws IOException {
     if (cds.size() <= 1 && hidden == 0) {
       // Skip sorting for singleton lists, to avoid WalkSorter opening the
       // repo just to fill out the commit field in PatchSetData.
@@ -175,8 +175,7 @@
     return sorted;
   }
 
-  private static List<ChangeData> ensureRequiredDataIsLoaded(List<ChangeData> cds)
-      throws OrmException {
+  private static List<ChangeData> ensureRequiredDataIsLoaded(List<ChangeData> cds) {
     // TODO(hiesel): Instead of calling these manually, either implement a helper that brings a
     // database-backed change on-par with an index-backed change in terms of the populated fields in
     // ChangeData or check if any of the ChangeDatas was loaded from the database and allow
diff --git a/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java b/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
index 708d914..f5a2751 100644
--- a/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
@@ -27,7 +27,6 @@
 import com.google.gerrit.server.permissions.RefPermission;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.restapi.change.ReviewersUtil.VisibilityControl;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -65,7 +64,7 @@
 
   @Override
   public List<SuggestedReviewerInfo> apply(ChangeResource rsrc)
-      throws AuthException, BadRequestException, OrmException, IOException, ConfigInvalidException,
+      throws AuthException, BadRequestException, IOException, ConfigInvalidException,
           PermissionBackendException {
     if (!self.get().isIdentifiedUser()) {
       throw new AuthException("Authentication required");
diff --git a/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java b/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
index 7cba8e7..4904da7 100644
--- a/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
+++ b/java/com/google/gerrit/server/restapi/change/TestSubmitRule.java
@@ -35,7 +35,6 @@
 import com.google.gerrit.server.rules.DefaultSubmitRule;
 import com.google.gerrit.server.rules.PrologRule;
 import com.google.gerrit.server.rules.RulesCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -70,7 +69,7 @@
 
   @Override
   public List<TestSubmitRuleInfo> apply(RevisionResource rsrc, TestSubmitRuleInput input)
-      throws AuthException, OrmException, PermissionBackendException, BadRequestException {
+      throws AuthException, PermissionBackendException, BadRequestException {
     if (input == null) {
       input = new TestSubmitRuleInput();
     }
diff --git a/java/com/google/gerrit/server/restapi/change/TestSubmitType.java b/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
index 684d22f..46dbad6 100644
--- a/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
+++ b/java/com/google/gerrit/server/restapi/change/TestSubmitType.java
@@ -28,7 +28,6 @@
 import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.rules.RulesCache;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import org.kohsuke.args4j.Option;
 
@@ -52,7 +51,7 @@
 
   @Override
   public SubmitType apply(RevisionResource rsrc, TestSubmitRuleInput input)
-      throws AuthException, BadRequestException, OrmException {
+      throws AuthException, BadRequestException {
     if (input == null) {
       input = new TestSubmitRuleInput();
     }
@@ -88,8 +87,7 @@
     }
 
     @Override
-    public SubmitType apply(RevisionResource resource)
-        throws AuthException, BadRequestException, OrmException {
+    public SubmitType apply(RevisionResource resource) throws AuthException, BadRequestException {
       return test.apply(resource, null);
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/change/Unignore.java b/java/com/google/gerrit/server/restapi/change/Unignore.java
index 6f2144a..26d3233 100644
--- a/java/com/google/gerrit/server/restapi/change/Unignore.java
+++ b/java/com/google/gerrit/server/restapi/change/Unignore.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.restapi.change;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestModifyView;
@@ -22,7 +23,6 @@
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.StarredChangesUtil.IllegalLabelException;
 import com.google.gerrit.server.change.ChangeResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -46,8 +46,7 @@
   }
 
   @Override
-  public Response<String> apply(ChangeResource rsrc, Input input)
-      throws OrmException, IllegalLabelException {
+  public Response<String> apply(ChangeResource rsrc, Input input) throws IllegalLabelException {
     if (isIgnored(rsrc)) {
       stars.unignore(rsrc);
     }
@@ -57,7 +56,7 @@
   private boolean isIgnored(ChangeResource rsrc) {
     try {
       return stars.isIgnored(rsrc);
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("failed to check ignored star");
     }
     return false;
diff --git a/java/com/google/gerrit/server/restapi/change/Votes.java b/java/com/google/gerrit/server/restapi/change/Votes.java
index a5d77e9..31efe54 100644
--- a/java/com/google/gerrit/server/restapi/change/Votes.java
+++ b/java/com/google/gerrit/server/restapi/change/Votes.java
@@ -26,7 +26,6 @@
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.change.ReviewerResource;
 import com.google.gerrit.server.change.VoteResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.Map;
@@ -55,7 +54,7 @@
 
   @Override
   public VoteResource parse(ReviewerResource reviewer, IdString id)
-      throws ResourceNotFoundException, OrmException, AuthException, MethodNotAllowedException {
+      throws ResourceNotFoundException, AuthException, MethodNotAllowedException {
     if (reviewer.getRevisionResource() != null && !reviewer.getRevisionResource().isCurrent()) {
       throw new MethodNotAllowedException("Cannot access on non-current patch set");
     }
@@ -72,8 +71,7 @@
     }
 
     @Override
-    public Map<String, Short> apply(ReviewerResource rsrc)
-        throws OrmException, MethodNotAllowedException {
+    public Map<String, Short> apply(ReviewerResource rsrc) throws MethodNotAllowedException {
       if (rsrc.getRevisionResource() != null && !rsrc.getRevisionResource().isCurrent()) {
         throw new MethodNotAllowedException("Cannot list votes on non-current patch set");
       }
diff --git a/java/com/google/gerrit/server/restapi/config/AgreementJson.java b/java/com/google/gerrit/server/restapi/config/AgreementJson.java
index 02e5f68..d5c085b 100644
--- a/java/com/google/gerrit/server/restapi/config/AgreementJson.java
+++ b/java/com/google/gerrit/server/restapi/config/AgreementJson.java
@@ -17,7 +17,8 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.ContributorAgreement;
 import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.common.AgreementInfo;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
@@ -25,7 +26,6 @@
 import com.google.gerrit.server.group.GroupResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.restapi.group.GroupJson;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
@@ -61,7 +61,7 @@
         GroupControl gc = genericGroupControlFactory.controlFor(user, autoVerifyGroup.getUUID());
         GroupResource group = new GroupResource(gc);
         info.autoVerifyGroup = groupJson.format(group);
-      } catch (NoSuchGroupException | OrmException e) {
+      } catch (NoSuchGroupException | StorageException e) {
         logger.atWarning().log(
             "autoverify group \"%s\" does not exist, referenced in CLA \"%s\"",
             autoVerifyGroup.getName(), ca.getName());
diff --git a/java/com/google/gerrit/server/restapi/config/CheckConsistency.java b/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
index a16736b..61d5c79 100644
--- a/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
+++ b/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.permissions.GlobalPermission;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -56,8 +55,7 @@
 
   @Override
   public ConsistencyCheckInfo apply(ConfigResource resource, ConsistencyCheckInput input)
-      throws RestApiException, IOException, OrmException, PermissionBackendException,
-          ConfigInvalidException {
+      throws RestApiException, IOException, PermissionBackendException, ConfigInvalidException {
     permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
 
     if (input == null
diff --git a/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java b/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java
index 5a1592f..152a4db 100644
--- a/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java
+++ b/java/com/google/gerrit/server/restapi/config/ConfirmEmail.java
@@ -26,7 +26,6 @@
 import com.google.gerrit.server.config.ConfigResource;
 import com.google.gerrit.server.mail.EmailTokenVerifier;
 import com.google.gerrit.server.restapi.config.ConfirmEmail.Input;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -55,8 +54,7 @@
 
   @Override
   public Response<?> apply(ConfigResource rsrc, Input input)
-      throws AuthException, UnprocessableEntityException, OrmException, IOException,
-          ConfigInvalidException {
+      throws AuthException, UnprocessableEntityException, IOException, ConfigInvalidException {
     CurrentUser user = self.get();
     if (!user.isIdentifiedUser()) {
       throw new AuthException("Authentication required");
diff --git a/java/com/google/gerrit/server/restapi/group/AddMembers.java b/java/com/google/gerrit/server/restapi/group/AddMembers.java
index 2d2d02b..a841897 100644
--- a/java/com/google/gerrit/server/restapi/group/AddMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/AddMembers.java
@@ -18,7 +18,7 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.client.AuthType;
 import com.google.gerrit.extensions.common.AccountInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -49,7 +49,6 @@
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.restapi.group.AddMembers.Input;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -113,9 +112,8 @@
 
   @Override
   public List<AccountInfo> apply(GroupResource resource, Input input)
-      throws AuthException, NotInternalGroupException, UnprocessableEntityException, OrmException,
-          IOException, ConfigInvalidException, ResourceNotFoundException,
-          PermissionBackendException {
+      throws AuthException, NotInternalGroupException, UnprocessableEntityException, IOException,
+          ConfigInvalidException, ResourceNotFoundException, PermissionBackendException {
     GroupDescription.Internal internalGroup =
         resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
     input = Input.init(input);
@@ -145,7 +143,7 @@
   }
 
   Account findAccount(String nameOrEmailOrId)
-      throws UnprocessableEntityException, OrmException, IOException, ConfigInvalidException {
+      throws UnprocessableEntityException, IOException, ConfigInvalidException {
     AccountResolver.Result result = accountResolver.resolve(nameOrEmailOrId);
     try {
       return result.asUnique().getAccount();
@@ -177,7 +175,7 @@
   }
 
   public void addMembers(AccountGroup.UUID groupUuid, Set<Account.Id> newMemberIds)
-      throws OrmException, IOException, NoSuchGroupException, ConfigInvalidException {
+      throws IOException, NoSuchGroupException, ConfigInvalidException {
     InternalGroupUpdate groupUpdate =
         InternalGroupUpdate.builder()
             .setMemberModification(memberIds -> Sets.union(memberIds, newMemberIds))
@@ -224,8 +222,8 @@
 
     @Override
     public AccountInfo apply(GroupResource resource, IdString id, Input input)
-        throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
-            IOException, ConfigInvalidException, PermissionBackendException {
+        throws AuthException, MethodNotAllowedException, ResourceNotFoundException, IOException,
+            ConfigInvalidException, PermissionBackendException {
       AddMembers.Input in = new AddMembers.Input();
       in._oneMember = id.get();
       try {
@@ -251,7 +249,7 @@
 
     @Override
     public AccountInfo apply(MemberResource resource, Input input)
-        throws OrmException, PermissionBackendException {
+        throws PermissionBackendException {
       // Do nothing, the user is already a member.
       return get.apply(resource);
     }
diff --git a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
index 9782ad3..9f9deff 100644
--- a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
@@ -19,7 +19,7 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.DefaultInput;
@@ -39,7 +39,6 @@
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.restapi.group.AddSubgroups.Input;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -93,7 +92,7 @@
 
   @Override
   public List<GroupInfo> apply(GroupResource resource, Input input)
-      throws NotInternalGroupException, AuthException, UnprocessableEntityException, OrmException,
+      throws NotInternalGroupException, AuthException, UnprocessableEntityException,
           ResourceNotFoundException, IOException, ConfigInvalidException,
           PermissionBackendException {
     GroupDescription.Internal group =
@@ -124,7 +123,7 @@
 
   private void addSubgroups(
       AccountGroup.UUID parentGroupUuid, Set<AccountGroup.UUID> newSubgroupUuids)
-      throws OrmException, NoSuchGroupException, IOException, ConfigInvalidException {
+      throws NoSuchGroupException, IOException, ConfigInvalidException {
     InternalGroupUpdate groupUpdate =
         InternalGroupUpdate.builder()
             .setSubgroupModification(subgroupUuids -> Sets.union(subgroupUuids, newSubgroupUuids))
@@ -144,8 +143,8 @@
 
     @Override
     public GroupInfo apply(GroupResource resource, IdString id, Input input)
-        throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
-            IOException, ConfigInvalidException, PermissionBackendException {
+        throws AuthException, MethodNotAllowedException, ResourceNotFoundException, IOException,
+            ConfigInvalidException, PermissionBackendException {
       AddSubgroups.Input in = new AddSubgroups.Input();
       in.groups = ImmutableList.of(id.get());
       try {
@@ -171,7 +170,7 @@
 
     @Override
     public GroupInfo apply(SubgroupResource resource, Input input)
-        throws OrmException, PermissionBackendException {
+        throws PermissionBackendException {
       // Do nothing, the group is already included.
       return get.get().apply(resource);
     }
diff --git a/java/com/google/gerrit/server/restapi/group/CreateGroup.java b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
index e6bd791..ffa462d 100644
--- a/java/com/google/gerrit/server/restapi/group/CreateGroup.java
+++ b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.exceptions.DuplicateKeyException;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.api.groups.GroupInput;
 import com.google.gerrit.extensions.client.ListGroupsOption;
@@ -54,8 +55,6 @@
 import com.google.gerrit.server.plugincontext.PluginSetContext;
 import com.google.gerrit.server.validators.GroupCreationValidationListener;
 import com.google.gerrit.server.validators.ValidationException;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -125,8 +124,8 @@
   @Override
   public GroupInfo apply(TopLevelResource resource, IdString id, GroupInput input)
       throws AuthException, BadRequestException, UnprocessableEntityException,
-          ResourceConflictException, OrmException, IOException, ConfigInvalidException,
-          ResourceNotFoundException, PermissionBackendException {
+          ResourceConflictException, IOException, ConfigInvalidException, ResourceNotFoundException,
+          PermissionBackendException {
     String name = id.get();
     if (input == null) {
       input = new GroupInput();
@@ -178,7 +177,7 @@
   }
 
   private InternalGroup createGroup(CreateGroupArgs createGroupArgs)
-      throws OrmException, ResourceConflictException, IOException, ConfigInvalidException {
+      throws ResourceConflictException, IOException, ConfigInvalidException {
 
     String nameLower = createGroupArgs.getGroupName().toLowerCase(Locale.US);
 
@@ -218,7 +217,7 @@
         members -> ImmutableSet.copyOf(createGroupArgs.initialMembers));
     try {
       return groupsUpdateProvider.get().createGroup(groupCreation, groupUpdateBuilder.build());
-    } catch (OrmDuplicateKeyException e) {
+    } catch (DuplicateKeyException e) {
       throw new ResourceConflictException(
           "group '" + createGroupArgs.getGroupName() + "' already exists");
     }
diff --git a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
index fca88e7..b448631 100644
--- a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
@@ -16,7 +16,7 @@
 
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -33,7 +33,6 @@
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
 import com.google.gerrit.server.restapi.group.AddMembers.Input;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -56,8 +55,8 @@
 
   @Override
   public Response<?> apply(GroupResource resource, Input input)
-      throws AuthException, NotInternalGroupException, UnprocessableEntityException, OrmException,
-          IOException, ConfigInvalidException, ResourceNotFoundException {
+      throws AuthException, NotInternalGroupException, UnprocessableEntityException, IOException,
+          ConfigInvalidException, ResourceNotFoundException {
     GroupDescription.Internal internalGroup =
         resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
     input = Input.init(input);
@@ -82,7 +81,7 @@
   }
 
   private void removeGroupMembers(AccountGroup.UUID groupUuid, Set<Account.Id> accountIds)
-      throws OrmException, IOException, NoSuchGroupException, ConfigInvalidException {
+      throws IOException, NoSuchGroupException, ConfigInvalidException {
     InternalGroupUpdate groupUpdate =
         InternalGroupUpdate.builder()
             .setMemberModification(memberIds -> Sets.difference(memberIds, accountIds))
@@ -102,8 +101,8 @@
 
     @Override
     public Response<?> apply(MemberResource resource, Input input)
-        throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException,
-            IOException, ConfigInvalidException, ResourceNotFoundException {
+        throws AuthException, MethodNotAllowedException, UnprocessableEntityException, IOException,
+            ConfigInvalidException, ResourceNotFoundException {
       AddMembers.Input in = new AddMembers.Input();
       in._oneMember = resource.getMember().getAccountId().toString();
       return delete.get().apply(resource, in);
diff --git a/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java b/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
index c486af4..5821be5 100644
--- a/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
@@ -17,7 +17,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -33,7 +33,6 @@
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
 import com.google.gerrit.server.restapi.group.AddSubgroups.Input;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -56,7 +55,7 @@
 
   @Override
   public Response<?> apply(GroupResource resource, Input input)
-      throws AuthException, NotInternalGroupException, UnprocessableEntityException, OrmException,
+      throws AuthException, NotInternalGroupException, UnprocessableEntityException,
           ResourceNotFoundException, IOException, ConfigInvalidException {
     GroupDescription.Internal internalGroup =
         resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
@@ -86,7 +85,7 @@
 
   private void removeSubgroups(
       AccountGroup.UUID parentGroupUuid, Set<AccountGroup.UUID> removedSubgroupUuids)
-      throws OrmException, NoSuchGroupException, IOException, ConfigInvalidException {
+      throws NoSuchGroupException, IOException, ConfigInvalidException {
     InternalGroupUpdate groupUpdate =
         InternalGroupUpdate.builder()
             .setSubgroupModification(
@@ -107,7 +106,7 @@
 
     @Override
     public Response<?> apply(SubgroupResource resource, Input input)
-        throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException,
+        throws AuthException, MethodNotAllowedException, UnprocessableEntityException,
             ResourceNotFoundException, IOException, ConfigInvalidException {
       AddSubgroups.Input in = new AddSubgroups.Input();
       in.groups = ImmutableList.of(resource.getMember().get());
diff --git a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
index dcdd8a8..1a781d9 100644
--- a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
+++ b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
@@ -36,7 +36,6 @@
 import com.google.gerrit.server.group.InternalGroupDescription;
 import com.google.gerrit.server.group.db.Groups;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -76,8 +75,8 @@
 
   @Override
   public List<? extends GroupAuditEventInfo> apply(GroupResource rsrc)
-      throws AuthException, NotInternalGroupException, OrmException, IOException,
-          ConfigInvalidException, PermissionBackendException {
+      throws AuthException, NotInternalGroupException, IOException, ConfigInvalidException,
+          PermissionBackendException {
     GroupDescription.Internal group =
         rsrc.asInternalGroup().orElseThrow(NotInternalGroupException::new);
     if (!rsrc.getControl().isOwner()) {
diff --git a/java/com/google/gerrit/server/restapi/group/GetDetail.java b/java/com/google/gerrit/server/restapi/group/GetDetail.java
index 75d1e34..c757383 100644
--- a/java/com/google/gerrit/server/restapi/group/GetDetail.java
+++ b/java/com/google/gerrit/server/restapi/group/GetDetail.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.group.GroupResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -33,7 +32,7 @@
   }
 
   @Override
-  public GroupInfo apply(GroupResource rsrc) throws OrmException, PermissionBackendException {
+  public GroupInfo apply(GroupResource rsrc) throws PermissionBackendException {
     return json.format(rsrc);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/GetGroup.java b/java/com/google/gerrit/server/restapi/group/GetGroup.java
index c6cddb6..3ae447b 100644
--- a/java/com/google/gerrit/server/restapi/group/GetGroup.java
+++ b/java/com/google/gerrit/server/restapi/group/GetGroup.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.group.GroupResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -32,7 +31,7 @@
   }
 
   @Override
-  public GroupInfo apply(GroupResource resource) throws OrmException, PermissionBackendException {
+  public GroupInfo apply(GroupResource resource) throws PermissionBackendException {
     return json.format(resource.getGroup());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/GetMember.java b/java/com/google/gerrit/server/restapi/group/GetMember.java
index 95063de..63a8a1b 100644
--- a/java/com/google/gerrit/server/restapi/group/GetMember.java
+++ b/java/com/google/gerrit/server/restapi/group/GetMember.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.server.account.AccountLoader;
 import com.google.gerrit.server.group.MemberResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -33,7 +32,7 @@
   }
 
   @Override
-  public AccountInfo apply(MemberResource rsrc) throws OrmException, PermissionBackendException {
+  public AccountInfo apply(MemberResource rsrc) throws PermissionBackendException {
     AccountLoader loader = infoFactory.create(true);
     AccountInfo info = loader.get(rsrc.getMember().getAccountId());
     loader.fill();
diff --git a/java/com/google/gerrit/server/restapi/group/GetOwner.java b/java/com/google/gerrit/server/restapi/group/GetOwner.java
index 0906ce6..0f0417e 100644
--- a/java/com/google/gerrit/server/restapi/group/GetOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/GetOwner.java
@@ -15,14 +15,13 @@
 package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.group.GroupResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -40,8 +39,7 @@
 
   @Override
   public GroupInfo apply(GroupResource resource)
-      throws NotInternalGroupException, ResourceNotFoundException, OrmException,
-          PermissionBackendException {
+      throws NotInternalGroupException, ResourceNotFoundException, PermissionBackendException {
     GroupDescription.Internal group =
         resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
     try {
diff --git a/java/com/google/gerrit/server/restapi/group/GetSubgroup.java b/java/com/google/gerrit/server/restapi/group/GetSubgroup.java
index 16e2739..4466180 100644
--- a/java/com/google/gerrit/server/restapi/group/GetSubgroup.java
+++ b/java/com/google/gerrit/server/restapi/group/GetSubgroup.java
@@ -18,7 +18,6 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.group.SubgroupResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
@@ -32,7 +31,7 @@
   }
 
   @Override
-  public GroupInfo apply(SubgroupResource rsrc) throws OrmException, PermissionBackendException {
+  public GroupInfo apply(SubgroupResource rsrc) throws PermissionBackendException {
     return json.format(rsrc.getMemberDescription());
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/group/GroupJson.java b/java/com/google/gerrit/server/restapi/group/GroupJson.java
index a51fad2..12b9d61 100644
--- a/java/com/google/gerrit/server/restapi/group/GroupJson.java
+++ b/java/com/google/gerrit/server/restapi/group/GroupJson.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.group.GroupResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.util.Collection;
@@ -76,18 +75,17 @@
     return this;
   }
 
-  public GroupInfo format(GroupResource rsrc) throws OrmException, PermissionBackendException {
+  public GroupInfo format(GroupResource rsrc) throws PermissionBackendException {
     return createGroupInfo(rsrc.getGroup(), rsrc::getControl);
   }
 
-  public GroupInfo format(GroupDescription.Basic group)
-      throws OrmException, PermissionBackendException {
+  public GroupInfo format(GroupDescription.Basic group) throws PermissionBackendException {
     return createGroupInfo(group, Suppliers.memoize(() -> groupControlFactory.controlFor(group)));
   }
 
   private GroupInfo createGroupInfo(
       GroupDescription.Basic group, Supplier<GroupControl> groupControlSupplier)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     GroupInfo info = createBasicGroupInfo(group);
 
     if (group instanceof GroupDescription.Internal) {
@@ -110,7 +108,7 @@
       GroupInfo info,
       GroupDescription.Internal internalGroup,
       Supplier<GroupControl> groupControlSupplier)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     info.description = Strings.emptyToNull(internalGroup.getDescription());
     info.groupId = internalGroup.getId().get();
 
diff --git a/java/com/google/gerrit/server/restapi/group/ListGroups.java b/java/com/google/gerrit/server/restapi/group/ListGroups.java
index 600379c..9f2a7b7 100644
--- a/java/com/google/gerrit/server/restapi/group/ListGroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListGroups.java
@@ -23,7 +23,7 @@
 import com.google.common.collect.Streams;
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.client.ListGroupsOption;
 import com.google.gerrit.extensions.client.ListOption;
 import com.google.gerrit.extensions.common.GroupInfo;
@@ -46,7 +46,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.restapi.account.GetGroups;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -249,8 +248,7 @@
 
   @Override
   public SortedMap<String, GroupInfo> apply(TopLevelResource resource)
-      throws OrmException, RestApiException, IOException, ConfigInvalidException,
-          PermissionBackendException {
+      throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     SortedMap<String, GroupInfo> output = new TreeMap<>();
     for (GroupInfo info : get()) {
       output.put(MoreObjects.firstNonNull(info.name, "Group " + Url.decode(info.id)), info);
@@ -260,8 +258,7 @@
   }
 
   public List<GroupInfo> get()
-      throws OrmException, RestApiException, IOException, ConfigInvalidException,
-          PermissionBackendException {
+      throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     if (!Strings.isNullOrEmpty(suggest)) {
       return suggestGroups();
     }
@@ -286,7 +283,7 @@
   }
 
   private List<GroupInfo> getAllGroups()
-      throws OrmException, IOException, ConfigInvalidException, PermissionBackendException {
+      throws IOException, ConfigInvalidException, PermissionBackendException {
     Pattern pattern = getRegexPattern();
     Stream<GroupDescription.Internal> existingGroups =
         getAllExistingGroups()
@@ -317,8 +314,7 @@
     return groups.getAllGroupReferences();
   }
 
-  private List<GroupInfo> suggestGroups()
-      throws OrmException, BadRequestException, PermissionBackendException {
+  private List<GroupInfo> suggestGroups() throws BadRequestException, PermissionBackendException {
     if (conflictingSuggestParameters()) {
       throw new BadRequestException(
           "You should only have no more than one --project and -n with --suggest");
@@ -374,7 +370,7 @@
   }
 
   private List<GroupInfo> filterGroupsOwnedBy(Predicate<GroupDescription.Internal> filter)
-      throws OrmException, IOException, ConfigInvalidException, PermissionBackendException {
+      throws IOException, ConfigInvalidException, PermissionBackendException {
     Pattern pattern = getRegexPattern();
     Stream<? extends GroupDescription.Internal> foundGroups =
         groups
@@ -402,14 +398,13 @@
   }
 
   private List<GroupInfo> getGroupsOwnedBy(String id)
-      throws OrmException, RestApiException, IOException, ConfigInvalidException,
-          PermissionBackendException {
+      throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     String uuid = groupResolver.parse(id).getGroupUUID().get();
     return filterGroupsOwnedBy(group -> group.getOwnerGroupUUID().get().equals(uuid));
   }
 
   private List<GroupInfo> getGroupsOwnedBy(IdentifiedUser user)
-      throws OrmException, IOException, ConfigInvalidException, PermissionBackendException {
+      throws IOException, ConfigInvalidException, PermissionBackendException {
     return filterGroupsOwnedBy(group -> isOwner(user, group));
   }
 
diff --git a/java/com/google/gerrit/server/restapi/group/ListMembers.java b/java/com/google/gerrit/server/restapi/group/ListMembers.java
index 4742644..8f58de2 100644
--- a/java/com/google/gerrit/server/restapi/group/ListMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/ListMembers.java
@@ -33,7 +33,6 @@
 import com.google.gerrit.server.group.InternalGroup;
 import com.google.gerrit.server.group.InternalGroupDescription;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -67,7 +66,7 @@
 
   @Override
   public List<AccountInfo> apply(GroupResource resource)
-      throws NotInternalGroupException, OrmException, PermissionBackendException {
+      throws NotInternalGroupException, PermissionBackendException {
     GroupDescription.Internal group =
         resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
     if (recursive) {
diff --git a/java/com/google/gerrit/server/restapi/group/ListSubgroups.java b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
index 864b01b..bb72a10 100644
--- a/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListSubgroups.java
@@ -19,14 +19,13 @@
 
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.group.GroupResource;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.ArrayList;
@@ -47,7 +46,7 @@
 
   @Override
   public List<GroupInfo> apply(GroupResource rsrc)
-      throws NotInternalGroupException, OrmException, PermissionBackendException {
+      throws NotInternalGroupException, PermissionBackendException {
     GroupDescription.Internal group =
         rsrc.asInternalGroup().orElseThrow(NotInternalGroupException::new);
 
@@ -56,7 +55,7 @@
 
   public List<GroupInfo> getDirectSubgroups(
       GroupDescription.Internal group, GroupControl groupControl)
-      throws OrmException, PermissionBackendException {
+      throws PermissionBackendException {
     boolean ownerOfParent = groupControl.isOwner();
     List<GroupInfo> included = new ArrayList<>();
     for (AccountGroup.UUID subgroupUuid : group.getSubgroups()) {
diff --git a/java/com/google/gerrit/server/restapi/group/MembersCollection.java b/java/com/google/gerrit/server/restapi/group/MembersCollection.java
index fec1443..6dfb2b6 100644
--- a/java/com/google/gerrit/server/restapi/group/MembersCollection.java
+++ b/java/com/google/gerrit/server/restapi/group/MembersCollection.java
@@ -26,7 +26,6 @@
 import com.google.gerrit.server.group.GroupResource;
 import com.google.gerrit.server.group.MemberResource;
 import com.google.gerrit.server.restapi.account.AccountsCollection;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -56,8 +55,8 @@
 
   @Override
   public MemberResource parse(GroupResource parent, IdString id)
-      throws NotInternalGroupException, AuthException, ResourceNotFoundException, OrmException,
-          IOException, ConfigInvalidException {
+      throws NotInternalGroupException, AuthException, ResourceNotFoundException, IOException,
+          ConfigInvalidException {
     GroupDescription.Internal group =
         parent.asInternalGroup().orElseThrow(NotInternalGroupException::new);
 
diff --git a/java/com/google/gerrit/server/restapi/group/PutDescription.java b/java/com/google/gerrit/server/restapi/group/PutDescription.java
index dbc124b..c9078b0 100644
--- a/java/com/google/gerrit/server/restapi/group/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/group/PutDescription.java
@@ -16,7 +16,7 @@
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.DescriptionInput;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -27,7 +27,6 @@
 import com.google.gerrit.server.group.GroupResource;
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -46,8 +45,8 @@
 
   @Override
   public Response<String> apply(GroupResource resource, DescriptionInput input)
-      throws AuthException, NotInternalGroupException, ResourceNotFoundException, OrmException,
-          IOException, ConfigInvalidException {
+      throws AuthException, NotInternalGroupException, ResourceNotFoundException, IOException,
+          ConfigInvalidException {
     if (input == null) {
       input = new DescriptionInput(); // Delete would set description to null.
     }
diff --git a/java/com/google/gerrit/server/restapi/group/PutName.java b/java/com/google/gerrit/server/restapi/group/PutName.java
index 1f1968a..319dc45 100644
--- a/java/com/google/gerrit/server/restapi/group/PutName.java
+++ b/java/com/google/gerrit/server/restapi/group/PutName.java
@@ -16,7 +16,8 @@
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.NameInput;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -28,7 +29,6 @@
 import com.google.gerrit.server.group.GroupResource;
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -79,7 +79,7 @@
       groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
     } catch (NoSuchGroupException e) {
       throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
-    } catch (OrmDuplicateKeyException e) {
+    } catch (DuplicateKeyException e) {
       throw new ResourceConflictException("group with name " + newName + " already exists");
     }
   }
diff --git a/java/com/google/gerrit/server/restapi/group/PutOptions.java b/java/com/google/gerrit/server/restapi/group/PutOptions.java
index 29b87d2..267f414 100644
--- a/java/com/google/gerrit/server/restapi/group/PutOptions.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOptions.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.server.restapi.group;
 
 import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.common.GroupOptionsInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -26,7 +26,6 @@
 import com.google.gerrit.server.group.GroupResource;
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -45,7 +44,7 @@
   @Override
   public GroupOptionsInfo apply(GroupResource resource, GroupOptionsInfo input)
       throws NotInternalGroupException, AuthException, BadRequestException,
-          ResourceNotFoundException, OrmException, IOException, ConfigInvalidException {
+          ResourceNotFoundException, IOException, ConfigInvalidException {
     GroupDescription.Internal internalGroup =
         resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
     if (!resource.getControl().isOwner()) {
diff --git a/java/com/google/gerrit/server/restapi/group/PutOwner.java b/java/com/google/gerrit/server/restapi/group/PutOwner.java
index 6ebec05..4cdad38 100644
--- a/java/com/google/gerrit/server/restapi/group/PutOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOwner.java
@@ -16,7 +16,7 @@
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.data.GroupDescription;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.api.groups.OwnerInput;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -31,7 +31,6 @@
 import com.google.gerrit.server.group.db.GroupsUpdate;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -57,8 +56,8 @@
   @Override
   public GroupInfo apply(GroupResource resource, OwnerInput input)
       throws ResourceNotFoundException, NotInternalGroupException, AuthException,
-          BadRequestException, UnprocessableEntityException, OrmException, IOException,
-          ConfigInvalidException, PermissionBackendException {
+          BadRequestException, UnprocessableEntityException, IOException, ConfigInvalidException,
+          PermissionBackendException {
     GroupDescription.Internal internalGroup =
         resource.asInternalGroup().orElseThrow(NotInternalGroupException::new);
     if (!resource.getControl().isOwner()) {
diff --git a/java/com/google/gerrit/server/restapi/group/QueryGroups.java b/java/com/google/gerrit/server/restapi/group/QueryGroups.java
index 6a7262b..3ab0720 100644
--- a/java/com/google/gerrit/server/restapi/group/QueryGroups.java
+++ b/java/com/google/gerrit/server/restapi/group/QueryGroups.java
@@ -30,7 +30,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.query.group.GroupQueryBuilder;
 import com.google.gerrit.server.query.group.GroupQueryProcessor;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.util.ArrayList;
 import java.util.EnumSet;
@@ -96,8 +95,7 @@
 
   @Override
   public List<GroupInfo> apply(TopLevelResource resource)
-      throws BadRequestException, MethodNotAllowedException, OrmException,
-          PermissionBackendException {
+      throws BadRequestException, MethodNotAllowedException, PermissionBackendException {
     if (Strings.isNullOrEmpty(query)) {
       throw new BadRequestException("missing query field");
     }
diff --git a/java/com/google/gerrit/server/restapi/project/CheckAccess.java b/java/com/google/gerrit/server/restapi/project/CheckAccess.java
index 51e0304..67b68b5 100644
--- a/java/com/google/gerrit/server/restapi/project/CheckAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckAccess.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.permissions.RefPermission;
 import com.google.gerrit.server.project.ProjectResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -61,8 +60,7 @@
 
   @Override
   public AccessCheckInfo apply(ProjectResource rsrc, AccessCheckInput input)
-      throws OrmException, PermissionBackendException, RestApiException, IOException,
-          ConfigInvalidException {
+      throws PermissionBackendException, RestApiException, IOException, ConfigInvalidException {
     permissionBackend.user(rsrc.getUser()).check(GlobalPermission.VIEW_ACCESS);
 
     rsrc.getProjectState().checkStatePermitsRead();
diff --git a/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java b/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java
index 0aab0e1..770e8c3 100644
--- a/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckAccessReadView.java
@@ -20,7 +20,6 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -50,8 +49,7 @@
 
   @Override
   public AccessCheckInfo apply(ProjectResource rsrc)
-      throws OrmException, PermissionBackendException, RestApiException, IOException,
-          ConfigInvalidException {
+      throws PermissionBackendException, RestApiException, IOException, ConfigInvalidException {
 
     AccessCheckInput input = new AccessCheckInput();
     input.ref = refName;
diff --git a/java/com/google/gerrit/server/restapi/project/CheckMergeability.java b/java/com/google/gerrit/server/restapi/project/CheckMergeability.java
index 3dafb39..de2ac64 100644
--- a/java/com/google/gerrit/server/restapi/project/CheckMergeability.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckMergeability.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.server.git.InMemoryInserter;
 import com.google.gerrit.server.git.MergeUtil;
 import com.google.gerrit.server.project.BranchResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import org.eclipse.jgit.lib.Config;
@@ -77,7 +76,7 @@
 
   @Override
   public MergeableInfo apply(BranchResource resource)
-      throws IOException, OrmException, BadRequestException, ResourceNotFoundException {
+      throws IOException, BadRequestException, ResourceNotFoundException {
     if (!(submitType.equals(SubmitType.MERGE_ALWAYS)
         || submitType.equals(SubmitType.MERGE_IF_NECESSARY))) {
       throw new BadRequestException("Submit type: " + submitType + " is not supported");
diff --git a/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java b/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
index 3855b78..8cc8298 100644
--- a/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
+++ b/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
@@ -20,7 +20,6 @@
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.change.IncludedIn;
 import com.google.gerrit.server.project.CommitResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -36,8 +35,7 @@
   }
 
   @Override
-  public IncludedInInfo apply(CommitResource rsrc)
-      throws RestApiException, OrmException, IOException {
+  public IncludedInInfo apply(CommitResource rsrc) throws RestApiException, IOException {
     RevCommit commit = rsrc.getCommit();
     Project.NameKey project = rsrc.getProjectState().getNameKey();
     return includedIn.apply(project, commit.getId().getName());
diff --git a/java/com/google/gerrit/server/restapi/project/CommitsCollection.java b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
index e8a12bc..25bdadb 100644
--- a/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
@@ -32,7 +32,6 @@
 import com.google.gerrit.server.project.Reachable;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -75,7 +74,7 @@
 
   @Override
   public CommitResource parse(ProjectResource parent, IdString id)
-      throws RestApiException, IOException, OrmException {
+      throws RestApiException, IOException {
     parent.getProjectState().checkStatePermitsRead();
     ObjectId objectId;
     try {
@@ -106,8 +105,7 @@
   }
 
   /** @return true if {@code commit} is visible to the caller. */
-  public boolean canRead(ProjectState state, Repository repo, RevCommit commit)
-      throws OrmException, IOException {
+  public boolean canRead(ProjectState state, Repository repo, RevCommit commit) throws IOException {
     Project.NameKey project = state.getNameKey();
     if (indexes.getSearchIndex() == null) {
       // No index in slaves, fall back to scanning refs.
diff --git a/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java b/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
index c3bbb60..ee3d0a8 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
@@ -16,7 +16,7 @@
 
 import com.google.common.collect.ImmutableMap;
 import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.errors.InvalidNameException;
+import com.google.gerrit.exceptions.InvalidNameException;
 import com.google.gerrit.extensions.api.access.ProjectAccessInput;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -43,7 +43,6 @@
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -93,7 +92,7 @@
   @Override
   public Response<ChangeInfo> apply(ProjectResource rsrc, ProjectAccessInput input)
       throws PermissionBackendException, AuthException, IOException, ConfigInvalidException,
-          OrmException, InvalidNameException, UpdateException, RestApiException {
+          InvalidNameException, UpdateException, RestApiException {
     PermissionBackend.ForProject forProject =
         permissionBackend.user(rsrc.getUser()).project(rsrc.getNameKey());
     if (!check(forProject, ProjectPermission.READ_CONFIG)) {
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteBranch.java b/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
index 0134ce3..3a2bd6f 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
@@ -26,7 +26,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.BranchResource;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -46,7 +45,7 @@
 
   @Override
   public Response<?> apply(BranchResource rsrc, Input input)
-      throws RestApiException, OrmException, IOException, PermissionBackendException {
+      throws RestApiException, IOException, PermissionBackendException {
     if (isConfigRef(rsrc.getBranchKey().get())) {
       // Never allow to delete the meta config branch.
       throw new MethodNotAllowedException(
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteBranches.java b/java/com/google/gerrit/server/restapi/project/DeleteBranches.java
index 6e60193..d429655 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteBranches.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteBranches.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -40,7 +39,7 @@
 
   @Override
   public Response<?> apply(ProjectResource project, DeleteBranchesInput input)
-      throws OrmException, IOException, RestApiException, PermissionBackendException {
+      throws IOException, RestApiException, PermissionBackendException {
     if (input == null || input.branches == null || input.branches.isEmpty()) {
       throw new BadRequestException("branches must be specified");
     }
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteRef.java b/java/com/google/gerrit/server/restapi/project/DeleteRef.java
index 5df102c..8861c0b 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteRef.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteRef.java
@@ -38,7 +38,6 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.project.RefValidationHelper;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -180,15 +179,13 @@
    * @param projectState the {@code ProjectState} of the project whose refs are to be deleted.
    * @param refsToDelete the refs to be deleted.
    * @param prefix the prefix of the refs.
-   * @throws OrmException
    * @throws IOException
    * @throws ResourceConflictException
    * @throws PermissionBackendException
    */
   public void deleteMultipleRefs(
       ProjectState projectState, ImmutableSet<String> refsToDelete, String prefix)
-      throws OrmException, IOException, ResourceConflictException, PermissionBackendException,
-          AuthException {
+      throws IOException, ResourceConflictException, PermissionBackendException, AuthException {
     if (refsToDelete.isEmpty()) {
       return;
     }
@@ -229,7 +226,7 @@
 
   private ReceiveCommand createDeleteCommand(
       ProjectState projectState, Repository r, String refName)
-      throws OrmException, IOException, ResourceConflictException, PermissionBackendException {
+      throws IOException, ResourceConflictException, PermissionBackendException {
     Ref ref = r.getRefDatabase().getRef(refName);
     ReceiveCommand command;
     if (ref == null) {
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteTag.java b/java/com/google/gerrit/server/restapi/project/DeleteTag.java
index f7cce11..33955ee 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteTag.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteTag.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.RefUtil;
 import com.google.gerrit.server.project.TagResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -41,7 +40,7 @@
 
   @Override
   public Response<?> apply(TagResource resource, Input input)
-      throws OrmException, RestApiException, IOException, PermissionBackendException {
+      throws RestApiException, IOException, PermissionBackendException {
     String tag = RefUtil.normalizeTagRef(resource.getTagInfo().ref);
 
     if (isConfigRef(tag)) {
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteTags.java b/java/com/google/gerrit/server/restapi/project/DeleteTags.java
index bf2c524..6e8ec37 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteTags.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteTags.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.extensions.restapi.RestModifyView;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -40,7 +39,7 @@
 
   @Override
   public Response<?> apply(ProjectResource project, DeleteTagsInput input)
-      throws OrmException, RestApiException, IOException, PermissionBackendException {
+      throws RestApiException, IOException, PermissionBackendException {
     if (input == null || input.tags == null || input.tags.isEmpty()) {
       throw new BadRequestException("tags must be specified");
     }
diff --git a/java/com/google/gerrit/server/restapi/project/GetAccess.java b/java/com/google/gerrit/server/restapi/project/GetAccess.java
index 559e8e1..744c8b2 100644
--- a/java/com/google/gerrit/server/restapi/project/GetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/GetAccess.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.common.data.GroupDescription;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
-import com.google.gerrit.common.data.RefConfigSection;
 import com.google.gerrit.extensions.api.access.AccessSectionInfo;
 import com.google.gerrit.extensions.api.access.PermissionInfo;
 import com.google.gerrit.extensions.api.access.PermissionRuleInfo;
@@ -196,7 +195,7 @@
           info.local.put(section.getName(), createAccessSection(groups, section));
         }
 
-      } else if (RefConfigSection.isValid(name)) {
+      } else if (AccessSection.isValidRefSectionName(name)) {
         if (check(perm, name, WRITE_CONFIG)) {
           info.local.put(name, createAccessSection(groups, section));
           info.ownerOf.add(name);
diff --git a/java/com/google/gerrit/server/restapi/project/GetHead.java b/java/com/google/gerrit/server/restapi/project/GetHead.java
index 5b10120..bc267c8 100644
--- a/java/com/google/gerrit/server/restapi/project/GetHead.java
+++ b/java/com/google/gerrit/server/restapi/project/GetHead.java
@@ -23,7 +23,6 @@
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.permissions.RefPermission;
 import com.google.gerrit.server.project.ProjectResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.io.IOException;
@@ -54,8 +53,7 @@
 
   @Override
   public String apply(ProjectResource rsrc)
-      throws AuthException, ResourceNotFoundException, IOException, OrmException,
-          PermissionBackendException {
+      throws AuthException, ResourceNotFoundException, IOException, PermissionBackendException {
     rsrc.getProjectState().statePermitsRead();
     try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) {
       Ref head = repo.getRefDatabase().exactRef(Constants.HEAD);
diff --git a/java/com/google/gerrit/server/restapi/project/Index.java b/java/com/google/gerrit/server/restapi/project/Index.java
index a346aed..bb88582 100644
--- a/java/com/google/gerrit/server/restapi/project/Index.java
+++ b/java/com/google/gerrit/server/restapi/project/Index.java
@@ -30,7 +30,6 @@
 import com.google.gerrit.server.index.IndexExecutor;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -58,7 +57,7 @@
 
   @Override
   public Response.Accepted apply(ProjectResource rsrc, IndexProjectInput input)
-      throws IOException, OrmException, PermissionBackendException, RestApiException {
+      throws IOException, PermissionBackendException, RestApiException {
     String response = "Project " + rsrc.getName() + " submitted for reindexing";
 
     reindex(rsrc.getNameKey(), input.async);
@@ -72,18 +71,10 @@
     return Response.accepted(response);
   }
 
-  private void reindex(Project.NameKey project, Boolean async) throws IOException {
+  private void reindex(Project.NameKey project, Boolean async) {
     if (Boolean.TRUE.equals(async)) {
       @SuppressWarnings("unused")
-      Future<?> possiblyIgnoredError =
-          executor.submit(
-              () -> {
-                try {
-                  indexer.index(project);
-                } catch (IOException e) {
-                  logger.atWarning().withCause(e).log("reindexing project %s failed", project);
-                }
-              });
+      Future<?> possiblyIgnoredError = executor.submit(() -> indexer.index(project));
     } else {
       indexer.index(project);
     }
diff --git a/java/com/google/gerrit/server/restapi/project/ListChildProjects.java b/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
index ea54a4a..3879720 100644
--- a/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
@@ -27,7 +27,6 @@
 import com.google.gerrit.server.permissions.ProjectPermission;
 import com.google.gerrit.server.project.ChildProjects;
 import com.google.gerrit.server.project.ProjectResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.util.List;
@@ -67,7 +66,7 @@
 
   @Override
   public List<ProjectInfo> apply(ProjectResource rsrc)
-      throws PermissionBackendException, OrmException, RestApiException {
+      throws PermissionBackendException, RestApiException {
     if (limit < 0) {
       throw new BadRequestException("limit must be a positive number");
     }
@@ -82,8 +81,7 @@
     return directChildProjects(rsrc.getNameKey());
   }
 
-  private List<ProjectInfo> directChildProjects(Project.NameKey parent)
-      throws OrmException, RestApiException {
+  private List<ProjectInfo> directChildProjects(Project.NameKey parent) throws RestApiException {
     PermissionBackend.WithUser currentUser = permissionBackend.currentUser();
     return queryProvider.get().withQuery("parent:" + parent.get()).withLimit(limit).apply().stream()
         .filter(
diff --git a/java/com/google/gerrit/server/restapi/project/ListProjects.java b/java/com/google/gerrit/server/restapi/project/ListProjects.java
index dc7e537..4f3dbb7 100644
--- a/java/com/google/gerrit/server/restapi/project/ListProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/ListProjects.java
@@ -27,7 +27,8 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.common.ProjectInfo;
 import com.google.gerrit.extensions.common.WebLinkInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -57,7 +58,6 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.util.TreeFormatter;
 import com.google.gson.reflect.TypeToken;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.BufferedWriter;
@@ -358,7 +358,7 @@
           .collect(
               ImmutableSortedMap.toImmutableSortedMap(
                   natural(), p -> p.name, p -> showDescription ? p : nullifyDescription(p)));
-    } catch (OrmException | MethodNotAllowedException e) {
+    } catch (StorageException | MethodNotAllowedException e) {
       logger.atWarning().withCause(e).log(
           "Internal error while processing the query '%s' request", query);
       throw new BadRequestException("Internal error while processing the query request");
@@ -378,7 +378,7 @@
         newProjectsNamesStream(query).forEach(out::println);
       }
       out.flush();
-    } catch (OrmException | MethodNotAllowedException e) {
+    } catch (StorageException | MethodNotAllowedException e) {
       logger.atWarning().withCause(e).log(
           "Internal error while processing the query '%s' request", query);
       throw new BadRequestException("Internal error while processing the query request");
@@ -386,7 +386,7 @@
   }
 
   private Stream<String> newProjectsNamesStream(String query)
-      throws OrmException, MethodNotAllowedException, BadRequestException {
+      throws MethodNotAllowedException, BadRequestException {
     Stream<String> projects =
         queryProjectsProvider.get().withQuery(query).apply().stream().map(p -> p.name).skip(start);
     if (limit > 0) {
diff --git a/java/com/google/gerrit/server/restapi/project/QueryProjects.java b/java/com/google/gerrit/server/restapi/project/QueryProjects.java
index 44432aa..8727df3 100644
--- a/java/com/google/gerrit/server/restapi/project/QueryProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/QueryProjects.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.project.ProjectJson;
 import com.google.gerrit.server.query.project.ProjectQueryBuilder;
 import com.google.gerrit.server.query.project.ProjectQueryProcessor;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.util.ArrayList;
 import java.util.List;
@@ -88,12 +87,11 @@
 
   @Override
   public List<ProjectInfo> apply(TopLevelResource resource)
-      throws BadRequestException, MethodNotAllowedException, OrmException {
+      throws BadRequestException, MethodNotAllowedException {
     return apply();
   }
 
-  public List<ProjectInfo> apply()
-      throws BadRequestException, MethodNotAllowedException, OrmException {
+  public List<ProjectInfo> apply() throws BadRequestException, MethodNotAllowedException {
     if (Strings.isNullOrEmpty(query)) {
       throw new BadRequestException("missing query field");
     }
diff --git a/java/com/google/gerrit/server/restapi/project/SetAccess.java b/java/com/google/gerrit/server/restapi/project/SetAccess.java
index 19e89a9..5277cf8 100644
--- a/java/com/google/gerrit/server/restapi/project/SetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/SetAccess.java
@@ -17,7 +17,7 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
 import com.google.gerrit.common.data.AccessSection;
-import com.google.gerrit.common.errors.InvalidNameException;
+import com.google.gerrit.exceptions.InvalidNameException;
 import com.google.gerrit.extensions.api.access.ProjectAccessInfo;
 import com.google.gerrit.extensions.api.access.ProjectAccessInput;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -38,7 +38,6 @@
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectConfig;
 import com.google.gerrit.server.project.ProjectResource;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -83,8 +82,7 @@
   @Override
   public ProjectAccessInfo apply(ProjectResource rsrc, ProjectAccessInput input)
       throws ResourceNotFoundException, ResourceConflictException, IOException, AuthException,
-          BadRequestException, UnprocessableEntityException, OrmException,
-          PermissionBackendException {
+          BadRequestException, UnprocessableEntityException, PermissionBackendException {
     MetaDataUpdate.User metaDataUpdateUser = metaDataUpdateFactory.get();
 
     ProjectConfig config;
diff --git a/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java b/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
index c8857a2..e206319 100644
--- a/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
+++ b/java/com/google/gerrit/server/restapi/project/SetAccessUtil.java
@@ -21,7 +21,7 @@
 import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule;
-import com.google.gerrit.common.errors.InvalidNameException;
+import com.google.gerrit.exceptions.InvalidNameException;
 import com.google.gerrit.extensions.api.access.AccessSectionInfo;
 import com.google.gerrit.extensions.api.access.PermissionInfo;
 import com.google.gerrit.extensions.api.access.PermissionRuleInfo;
@@ -146,7 +146,7 @@
       boolean isGlobalCapabilities = AccessSection.GLOBAL_CAPABILITIES.equals(name);
 
       if (!isGlobalCapabilities) {
-        if (!AccessSection.isValid(name)) {
+        if (!AccessSection.isValidRefSectionName(name)) {
           throw new BadRequestException("invalid section name");
         }
         RefPattern.validate(name);
diff --git a/java/com/google/gerrit/server/rules/DefaultSubmitRule.java b/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
index b70858d..2be6c19 100644
--- a/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
+++ b/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.common.data.LabelFunction;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.annotations.Exports;
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
@@ -27,7 +28,6 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.ArrayList;
@@ -80,7 +80,7 @@
     try {
       labelTypes = cd.getLabelTypes().getLabelTypes();
       approvals = cd.currentApprovals();
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log(
           "Unable to fetch labels and approvals for change %s", cd.getId());
 
diff --git a/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
index f4b7254..592c269c 100644
--- a/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
+++ b/java/com/google/gerrit/server/rules/IgnoreSelfApprovalRule.java
@@ -23,13 +23,13 @@
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.common.data.SubmitRequirement;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.annotations.Exports;
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.util.ArrayList;
@@ -66,7 +66,7 @@
     try {
       labelTypes = cd.getLabelTypes().getLabelTypes();
       approvals = cd.currentApprovals();
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atWarning().withCause(e).log(E_UNABLE_TO_FETCH_LABELS);
       return singletonRuleError(E_UNABLE_TO_FETCH_LABELS);
     }
@@ -80,7 +80,7 @@
     Account.Id uploader;
     try {
       uploader = cd.currentPatchSet().getUploader();
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atWarning().withCause(e).log(E_UNABLE_TO_FETCH_UPLOADER);
       return singletonRuleError(E_UNABLE_TO_FETCH_UPLOADER);
     }
diff --git a/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java b/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
index edc2674..9cde54c 100644
--- a/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
+++ b/java/com/google/gerrit/server/rules/PrologRuleEvaluator.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
@@ -36,7 +37,6 @@
 import com.google.gerrit.server.project.RuleEvalException;
 import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
 import com.googlecode.prolog_cafe.exceptions.CompileException;
@@ -149,13 +149,13 @@
     try {
       change = cd.change();
       if (change == null) {
-        throw new OrmException("No change found");
+        throw new StorageException("No change found");
       }
 
       if (projectState == null) {
         throw new NoSuchProjectException(cd.project());
       }
-    } catch (OrmException | NoSuchProjectException e) {
+    } catch (StorageException | NoSuchProjectException e) {
       return ruleError("Error looking up change " + cd.getId(), e);
     }
 
diff --git a/java/com/google/gerrit/server/rules/StoredValues.java b/java/com/google/gerrit/server/rules/StoredValues.java
index 1c28c54..9bae091 100644
--- a/java/com/google/gerrit/server/rules/StoredValues.java
+++ b/java/com/google/gerrit/server/rules/StoredValues.java
@@ -16,6 +16,7 @@
 
 import static com.google.gerrit.server.rules.StoredValue.create;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
@@ -36,7 +37,6 @@
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import com.googlecode.prolog_cafe.exceptions.SystemException;
 import com.googlecode.prolog_cafe.lang.Prolog;
 import java.io.IOException;
@@ -57,7 +57,7 @@
     ChangeData cd = CHANGE_DATA.get(engine);
     try {
       return cd.change();
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       throw new SystemException("Cannot load change " + cd.getId());
     }
   }
@@ -66,7 +66,7 @@
     ChangeData cd = CHANGE_DATA.get(engine);
     try {
       return cd.currentPatchSet();
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       throw new SystemException(e.getMessage());
     }
   }
diff --git a/java/com/google/gerrit/server/schema/AllProjectsCreator.java b/java/com/google/gerrit/server/schema/AllProjectsCreator.java
index 1b44f39..9446b7c 100644
--- a/java/com/google/gerrit/server/schema/AllProjectsCreator.java
+++ b/java/com/google/gerrit/server/schema/AllProjectsCreator.java
@@ -41,7 +41,6 @@
 import com.google.gerrit.server.notedb.RepoSequence;
 import com.google.gerrit.server.notedb.Sequences;
 import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -86,8 +85,7 @@
     this.owners = systemGroupBackend.getGroup(PROJECT_OWNERS);
   }
 
-  public void create(AllProjectsInput input)
-      throws IOException, ConfigInvalidException, OrmException {
+  public void create(AllProjectsInput input) throws IOException, ConfigInvalidException {
     try (Repository git = repositoryManager.openRepository(allProjectsName)) {
       initAllProjects(git, input);
     } catch (RepositoryNotFoundException notFound) {
@@ -105,7 +103,7 @@
   }
 
   private void initAllProjects(Repository git, AllProjectsInput input)
-      throws IOException, ConfigInvalidException, OrmException {
+      throws ConfigInvalidException, IOException {
     BatchRefUpdate bru = git.getRefDatabase().newBatchUpdate();
     try (MetaDataUpdate md =
         new MetaDataUpdate(GitReferenceUpdated.DISABLED, allProjectsName, git, bru)) {
diff --git a/java/com/google/gerrit/server/schema/BUILD b/java/com/google/gerrit/server/schema/BUILD
index 93cad19..2f0b649 100644
--- a/java/com/google/gerrit/server/schema/BUILD
+++ b/java/com/google/gerrit/server/schema/BUILD
@@ -7,6 +7,7 @@
     deps = [
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/git",
         "//java/com/google/gerrit/lifecycle",
@@ -16,7 +17,6 @@
         "//java/com/google/gerrit/server/util/time",
         "//java/org/eclipse/jgit:server",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib/auto:auto-value",
         "//lib/auto:auto-value-annotations",
         "//lib/commons:dbcp",
diff --git a/java/com/google/gerrit/server/schema/H2AccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/H2AccountPatchReviewStore.java
index 5146140..3f74cda 100644
--- a/java/com/google/gerrit/server/schema/H2AccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/schema/H2AccountPatchReviewStore.java
@@ -14,11 +14,11 @@
 
 package com.google.gerrit.server.schema;
 
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.sql.SQLException;
@@ -36,17 +36,17 @@
   }
 
   @Override
-  public OrmException convertError(String op, SQLException err) {
+  public StorageException convertError(String op, SQLException err) {
     switch (getSQLStateInt(err)) {
       case 23001: // UNIQUE CONSTRAINT VIOLATION
       case 23505: // DUPLICATE_KEY_1
-        return new OrmDuplicateKeyException("account_patch_reviews", err);
+        return new DuplicateKeyException("account_patch_reviews", err);
 
       default:
         if (err.getCause() == null && err.getNextException() != null) {
           err.initCause(err.getNextException());
         }
-        return new OrmException(op + " failure on account_patch_reviews", err);
+        return new StorageException(op + " failure on account_patch_reviews", err);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
index fa4de40..1b48b2c 100644
--- a/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
@@ -21,6 +21,8 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.flogger.FluentLogger;
 import com.google.common.primitives.Ints;
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.events.LifecycleListener;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.lifecycle.LifecycleModule;
@@ -31,8 +33,6 @@
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
 import java.nio.file.Path;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
@@ -167,7 +167,7 @@
   public void start() {
     try {
       createTableIfNotExists();
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atSevere().withCause(e).log("Failed to create table to store account patch reviews");
     }
   }
@@ -176,7 +176,7 @@
     return ds.getConnection();
   }
 
-  public void createTableIfNotExists() throws OrmException {
+  public void createTableIfNotExists() {
     try (Connection con = ds.getConnection();
         Statement stmt = con.createStatement()) {
       doCreateTable(stmt);
@@ -197,7 +197,7 @@
             + ")");
   }
 
-  public void dropTableIfExists() throws OrmException {
+  public void dropTableIfExists() {
     try (Connection con = ds.getConnection();
         Statement stmt = con.createStatement()) {
       stmt.executeUpdate("DROP TABLE IF EXISTS account_patch_reviews");
@@ -210,8 +210,7 @@
   public void stop() {}
 
   @Override
-  public boolean markReviewed(PatchSet.Id psId, Account.Id accountId, String path)
-      throws OrmException {
+  public boolean markReviewed(PatchSet.Id psId, Account.Id accountId, String path) {
     try (Connection con = ds.getConnection();
         PreparedStatement stmt =
             con.prepareStatement(
@@ -225,8 +224,8 @@
       stmt.executeUpdate();
       return true;
     } catch (SQLException e) {
-      OrmException ormException = convertError("insert", e);
-      if (ormException instanceof OrmDuplicateKeyException) {
+      StorageException ormException = convertError("insert", e);
+      if (ormException instanceof DuplicateKeyException) {
         return false;
       }
       throw ormException;
@@ -234,8 +233,7 @@
   }
 
   @Override
-  public void markReviewed(PatchSet.Id psId, Account.Id accountId, Collection<String> paths)
-      throws OrmException {
+  public void markReviewed(PatchSet.Id psId, Account.Id accountId, Collection<String> paths) {
     if (paths == null || paths.isEmpty()) {
       return;
     }
@@ -255,8 +253,8 @@
       }
       stmt.executeBatch();
     } catch (SQLException e) {
-      OrmException ormException = convertError("insert", e);
-      if (ormException instanceof OrmDuplicateKeyException) {
+      StorageException ormException = convertError("insert", e);
+      if (ormException instanceof DuplicateKeyException) {
         return;
       }
       throw ormException;
@@ -264,8 +262,7 @@
   }
 
   @Override
-  public void clearReviewed(PatchSet.Id psId, Account.Id accountId, String path)
-      throws OrmException {
+  public void clearReviewed(PatchSet.Id psId, Account.Id accountId, String path) {
     try (Connection con = ds.getConnection();
         PreparedStatement stmt =
             con.prepareStatement(
@@ -283,7 +280,7 @@
   }
 
   @Override
-  public void clearReviewed(PatchSet.Id psId) throws OrmException {
+  public void clearReviewed(PatchSet.Id psId) {
     try (Connection con = ds.getConnection();
         PreparedStatement stmt =
             con.prepareStatement(
@@ -298,8 +295,7 @@
   }
 
   @Override
-  public Optional<PatchSetWithReviewedFiles> findReviewed(PatchSet.Id psId, Account.Id accountId)
-      throws OrmException {
+  public Optional<PatchSetWithReviewedFiles> findReviewed(PatchSet.Id psId, Account.Id accountId) {
     try (Connection con = ds.getConnection();
         PreparedStatement stmt =
             con.prepareStatement(
@@ -331,11 +327,11 @@
     }
   }
 
-  public OrmException convertError(String op, SQLException err) {
+  public StorageException convertError(String op, SQLException err) {
     if (err.getCause() == null && err.getNextException() != null) {
       err.initCause(err.getNextException());
     }
-    return new OrmException(op + " failure on account_patch_reviews", err);
+    return new StorageException(op + " failure on account_patch_reviews", err);
   }
 
   private static String getSQLState(SQLException err) {
diff --git a/java/com/google/gerrit/server/schema/MariaDBAccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/MariaDBAccountPatchReviewStore.java
index aa05a08..b0a3370 100644
--- a/java/com/google/gerrit/server/schema/MariaDBAccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/schema/MariaDBAccountPatchReviewStore.java
@@ -14,11 +14,11 @@
 
 package com.google.gerrit.server.schema;
 
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.sql.SQLException;
@@ -36,18 +36,18 @@
   }
 
   @Override
-  public OrmException convertError(String op, SQLException err) {
+  public StorageException convertError(String op, SQLException err) {
     switch (getSQLStateInt(err)) {
       case 1022: // ER_DUP_KEY
       case 1062: // ER_DUP_ENTRY
       case 1169: // ER_DUP_UNIQUE;
-        return new OrmDuplicateKeyException("ACCOUNT_PATCH_REVIEWS", err);
+        return new DuplicateKeyException("ACCOUNT_PATCH_REVIEWS", err);
 
       default:
         if (err.getCause() == null && err.getNextException() != null) {
           err.initCause(err.getNextException());
         }
-        return new OrmException(op + " failure on ACCOUNT_PATCH_REVIEWS", err);
+        return new StorageException(op + " failure on ACCOUNT_PATCH_REVIEWS", err);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/schema/MysqlAccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/MysqlAccountPatchReviewStore.java
index d648ed0..35bf2cb 100644
--- a/java/com/google/gerrit/server/schema/MysqlAccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/schema/MysqlAccountPatchReviewStore.java
@@ -14,11 +14,11 @@
 
 package com.google.gerrit.server.schema;
 
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.sql.SQLException;
@@ -37,18 +37,18 @@
   }
 
   @Override
-  public OrmException convertError(String op, SQLException err) {
+  public StorageException convertError(String op, SQLException err) {
     switch (getSQLStateInt(err)) {
       case 1022: // ER_DUP_KEY
       case 1062: // ER_DUP_ENTRY
       case 1169: // ER_DUP_UNIQUE;
-        return new OrmDuplicateKeyException("ACCOUNT_PATCH_REVIEWS", err);
+        return new DuplicateKeyException("ACCOUNT_PATCH_REVIEWS", err);
 
       default:
         if (err.getCause() == null && err.getNextException() != null) {
           err.initCause(err.getNextException());
         }
-        return new OrmException(op + " failure on ACCOUNT_PATCH_REVIEWS", err);
+        return new StorageException(op + " failure on ACCOUNT_PATCH_REVIEWS", err);
     }
   }
 
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java b/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
index f8a9cf7..3d08a73 100644
--- a/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaUpdater.java
@@ -20,12 +20,12 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSortedMap;
 import com.google.common.collect.ImmutableSortedSet;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.Sequences;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.stream.IntStream;
@@ -77,7 +77,7 @@
     this.schemaVersions = schemaVersions;
   }
 
-  public void update(UpdateUI ui) throws OrmException {
+  public void update(UpdateUI ui) {
     ensureSchemaCreated();
 
     int currentVersion = versionManager.read();
@@ -94,17 +94,17 @@
         NoteDbSchemaVersions.get(schemaVersions, nextVersion).upgrade(args, ui);
         versionManager.increment(nextVersion - 1);
       } catch (Exception e) {
-        throw new OrmException(
+        throw new StorageException(
             String.format("Failed to upgrade to schema version %d", nextVersion), e);
       }
     }
   }
 
-  private void ensureSchemaCreated() throws OrmException {
+  private void ensureSchemaCreated() {
     try {
       schemaCreator.ensureCreated();
     } catch (IOException | ConfigInvalidException e) {
-      throw new OrmException("Cannot initialize Gerrit site");
+      throw new StorageException("Cannot initialize Gerrit site");
     }
   }
 
@@ -114,7 +114,7 @@
     NOTE_DB
   }
 
-  private void checkNoteDbConfigFor216() throws OrmException {
+  private void checkNoteDbConfigFor216() {
     // Check that the NoteDb migration config matches what we expect from a site that both:
     // * Completed the change migration to NoteDB.
     // * Ran schema upgrades from a 2.16 final release.
@@ -125,7 +125,7 @@
                 "noteDb", "changes", "primaryStorage", PrimaryStorageFor216Compatibility.REVIEW_DB)
             != PrimaryStorageFor216Compatibility.NOTE_DB
         || !cfg.getBoolean("noteDb", "changes", "disableReviewDb", false)) {
-      throw new OrmException(
+      throw new StorageException(
           "You appear to be upgrading from a 2.x site, but the NoteDb change migration was"
               + " not completed. See documentation:\n"
               + "https://gerrit-review.googlesource.com/Documentation/note-db.html#migration");
@@ -149,24 +149,24 @@
     //    this and get 2.16 running rather than abandoning 2.16 and jumping to 3.0 at this point.
     try (Repository allUsers = repoManager.openRepository(allUsersName)) {
       if (allUsers.exactRef(RefNames.REFS_SEQUENCES + Sequences.NAME_GROUPS) == null) {
-        throw new OrmException(
+        throw new StorageException(
             "You appear to be upgrading to 3.x from a version prior to 2.16; you must upgrade to"
                 + " 2.16.x first");
       }
     } catch (IOException e) {
-      throw new OrmException("Failed to check NoteDb migration state", e);
+      throw new StorageException("Failed to check NoteDb migration state", e);
     }
   }
 
   @VisibleForTesting
   static ImmutableList<Integer> requiredUpgrades(
-      int currentVersion, ImmutableSortedSet<Integer> allVersions) throws OrmException {
+      int currentVersion, ImmutableSortedSet<Integer> allVersions) {
     int firstVersion = allVersions.first();
     int latestVersion = allVersions.last();
     if (currentVersion == latestVersion) {
       return ImmutableList.of();
     } else if (currentVersion > latestVersion) {
-      throw new OrmException(
+      throw new StorageException(
           String.format(
               "Cannot downgrade NoteDb schema from version %d to %d",
               currentVersion, latestVersion));
@@ -178,7 +178,7 @@
       firstUpgradeVersion = firstVersion;
     } else {
       if (currentVersion < firstVersion - 1) {
-        throw new OrmException(
+        throw new StorageException(
             String.format(
                 "Cannot skip NoteDb schema from version %d to %d", currentVersion, firstVersion));
       }
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaVersionCheck.java b/java/com/google/gerrit/server/schema/NoteDbSchemaVersionCheck.java
index 63bffec..33534fc 100644
--- a/java/com/google/gerrit/server/schema/NoteDbSchemaVersionCheck.java
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaVersionCheck.java
@@ -14,10 +14,10 @@
 
 package com.google.gerrit.server.schema;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.events.LifecycleListener;
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.server.config.SitePaths;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Module;
 import com.google.inject.ProvisionException;
@@ -65,7 +65,7 @@
                 "Unsupported schema version %d; expected schema version %d. %s",
                 current, expected, advice));
       }
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       throw new ProvisionException("Failed to read NoteDb schema version", e);
     }
   }
diff --git a/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java b/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java
index da5f7b9..7ff0ea6 100644
--- a/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java
+++ b/java/com/google/gerrit/server/schema/NoteDbSchemaVersionManager.java
@@ -17,11 +17,11 @@
 import static com.google.gerrit.reviewdb.client.RefNames.REFS_VERSION;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.notedb.IntBlob;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.Optional;
@@ -45,20 +45,20 @@
     this.repoManager = repoManager;
   }
 
-  public int read() throws OrmException {
+  public int read() {
     try (Repository repo = repoManager.openRepository(allProjectsName)) {
       return IntBlob.parse(repo, REFS_VERSION).map(IntBlob::value).orElse(0);
     } catch (IOException e) {
-      throw new OrmException("Failed to read " + REFS_VERSION, e);
+      throw new StorageException("Failed to read " + REFS_VERSION, e);
     }
   }
 
-  public void init() throws IOException, OrmException {
+  public void init() throws IOException {
     try (Repository repo = repoManager.openRepository(allProjectsName);
         RevWalk rw = new RevWalk(repo)) {
       Optional<IntBlob> old = IntBlob.parse(repo, REFS_VERSION, rw);
       if (old.isPresent()) {
-        throw new OrmException(
+        throw new StorageException(
             String.format(
                 "Expected no old version for %s, found %s", REFS_VERSION, old.get().value()));
       }
@@ -73,12 +73,12 @@
     }
   }
 
-  public void increment(int expectedOldVersion) throws IOException, OrmException {
+  public void increment(int expectedOldVersion) throws IOException {
     try (Repository repo = repoManager.openRepository(allProjectsName);
         RevWalk rw = new RevWalk(repo)) {
       Optional<IntBlob> old = IntBlob.parse(repo, REFS_VERSION, rw);
       if (old.isPresent() && old.get().value() != expectedOldVersion) {
-        throw new OrmException(
+        throw new StorageException(
             String.format(
                 "Expected old version %d for %s, found %d",
                 expectedOldVersion, REFS_VERSION, old.get().value()));
diff --git a/java/com/google/gerrit/server/schema/PostgresqlAccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/PostgresqlAccountPatchReviewStore.java
index 34f7dba..db68f2e 100644
--- a/java/com/google/gerrit/server/schema/PostgresqlAccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/schema/PostgresqlAccountPatchReviewStore.java
@@ -14,11 +14,11 @@
 
 package com.google.gerrit.server.schema;
 
+import com.google.gerrit.exceptions.DuplicateKeyException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.config.ThreadSettingsConfig;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import java.sql.SQLException;
@@ -36,10 +36,10 @@
   }
 
   @Override
-  public OrmException convertError(String op, SQLException err) {
+  public StorageException convertError(String op, SQLException err) {
     switch (getSQLStateInt(err)) {
       case 23505: // DUPLICATE_KEY_1
-        return new OrmDuplicateKeyException("ACCOUNT_PATCH_REVIEWS", err);
+        return new DuplicateKeyException("ACCOUNT_PATCH_REVIEWS", err);
 
       case 23514: // CHECK CONSTRAINT VIOLATION
       case 23503: // FOREIGN KEY CONSTRAINT VIOLATION
@@ -49,7 +49,7 @@
         if (err.getCause() == null && err.getNextException() != null) {
           err.initCause(err.getNextException());
         }
-        return new OrmException(op + " failure on ACCOUNT_PATCH_REVIEWS", err);
+        return new StorageException(op + " failure on ACCOUNT_PATCH_REVIEWS", err);
     }
   }
 }
diff --git a/java/com/google/gerrit/server/schema/ProjectConfigSchemaUpdate.java b/java/com/google/gerrit/server/schema/ProjectConfigSchemaUpdate.java
index 483e363..9e12807 100644
--- a/java/com/google/gerrit/server/schema/ProjectConfigSchemaUpdate.java
+++ b/java/com/google/gerrit/server/schema/ProjectConfigSchemaUpdate.java
@@ -20,13 +20,13 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.PermissionRule;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gerrit.server.git.meta.VersionedMetaData;
 import com.google.gerrit.server.project.ProjectConfig;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.Arrays;
@@ -115,7 +115,7 @@
     return true;
   }
 
-  public void save(PersonIdent personIdent, String commitMessage) throws OrmException {
+  public void save(PersonIdent personIdent, String commitMessage) {
     if (!updated) {
       return;
     }
@@ -126,7 +126,7 @@
     try {
       commit(update);
     } catch (IOException e) {
-      throw new OrmException(e);
+      throw new StorageException(e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/schema/SchemaCreator.java b/java/com/google/gerrit/server/schema/SchemaCreator.java
index 8cf8fe7..b78ce73 100644
--- a/java/com/google/gerrit/server/schema/SchemaCreator.java
+++ b/java/com/google/gerrit/server/schema/SchemaCreator.java
@@ -14,7 +14,6 @@
 
 package com.google.gerrit.server.schema;
 
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 import org.eclipse.jgit.errors.ConfigInvalidException;
 
@@ -26,20 +25,18 @@
    *
    * <p>Fails if the schema does exist.
    *
-   * @throws OrmException an error occurred.
    * @throws IOException an error occurred.
    * @throws ConfigInvalidException an error occurred.
    */
-  void create() throws OrmException, IOException, ConfigInvalidException;
+  void create() throws IOException, ConfigInvalidException;
 
   /**
    * Create the schema only if it does not already exist.
    *
    * <p>Succeeds if the schema does exist.
    *
-   * @throws OrmException an error occurred.
    * @throws IOException an error occurred.
    * @throws ConfigInvalidException an error occurred.
    */
-  void ensureCreated() throws OrmException, IOException, ConfigInvalidException;
+  void ensureCreated() throws IOException, ConfigInvalidException;
 }
diff --git a/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java b/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
index 3605dd5..c100637 100644
--- a/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
+++ b/java/com/google/gerrit/server/schema/SchemaCreatorImpl.java
@@ -17,6 +17,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.exceptions.DuplicateKeyException;
 import com.google.gerrit.git.RefUpdateUtil;
 import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -36,8 +37,6 @@
 import com.google.gerrit.server.index.group.GroupIndex;
 import com.google.gerrit.server.index.group.GroupIndexCollection;
 import com.google.gerrit.server.notedb.Sequences;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -90,7 +89,7 @@
   }
 
   @Override
-  public void create() throws OrmException, IOException, ConfigInvalidException {
+  public void create() throws IOException, ConfigInvalidException {
     GroupReference admins = createGroupReference("Administrators");
     GroupReference batchUsers = createGroupReference("Non-Interactive Users");
 
@@ -117,7 +116,7 @@
   }
 
   @Override
-  public void ensureCreated() throws OrmException, IOException, ConfigInvalidException {
+  public void ensureCreated() throws IOException, ConfigInvalidException {
     try {
       repoManager.openRepository(allProjectsName).close();
     } catch (RepositoryNotFoundException e) {
@@ -127,7 +126,7 @@
 
   private void createAdminsGroup(
       Sequences seqs, Repository allUsersRepo, GroupReference groupReference)
-      throws OrmException, IOException, ConfigInvalidException {
+      throws IOException, ConfigInvalidException {
     InternalGroupCreation groupCreation = getGroupCreation(seqs, groupReference);
     InternalGroupUpdate groupUpdate =
         InternalGroupUpdate.builder().setDescription("Gerrit Site Administrators").build();
@@ -140,7 +139,7 @@
       Repository allUsersRepo,
       GroupReference groupReference,
       AccountGroup.UUID adminsGroupUuid)
-      throws OrmException, IOException, ConfigInvalidException {
+      throws IOException, ConfigInvalidException {
     InternalGroupCreation groupCreation = getGroupCreation(seqs, groupReference);
     InternalGroupUpdate groupUpdate =
         InternalGroupUpdate.builder()
@@ -153,14 +152,14 @@
 
   private void createGroup(
       Repository allUsersRepo, InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
-      throws OrmException, ConfigInvalidException, IOException {
+      throws ConfigInvalidException, IOException {
     InternalGroup createdGroup = createGroupInNoteDb(allUsersRepo, groupCreation, groupUpdate);
     index(createdGroup);
   }
 
   private InternalGroup createGroupInNoteDb(
       Repository allUsersRepo, InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
-      throws ConfigInvalidException, IOException, OrmDuplicateKeyException {
+      throws ConfigInvalidException, IOException, DuplicateKeyException {
     // This method is only executed on a new server which doesn't have any accounts or groups.
     AuditLogFormatter auditLogFormatter =
         AuditLogFormatter.createBackedBy(ImmutableSet.of(), ImmutableSet.of(), serverId);
@@ -216,8 +215,7 @@
     return new GroupReference(groupUuid, name);
   }
 
-  private InternalGroupCreation getGroupCreation(Sequences seqs, GroupReference groupReference)
-      throws OrmException {
+  private InternalGroupCreation getGroupCreation(Sequences seqs, GroupReference groupReference) {
     int next = seqs.nextGroupId();
     return InternalGroupCreation.builder()
         .setNameKey(new AccountGroup.NameKey(groupReference.getName()))
diff --git a/java/com/google/gerrit/server/ssh/NoSshKeyCache.java b/java/com/google/gerrit/server/ssh/NoSshKeyCache.java
index 387242c..74bb50c 100644
--- a/java/com/google/gerrit/server/ssh/NoSshKeyCache.java
+++ b/java/com/google/gerrit/server/ssh/NoSshKeyCache.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.ssh;
 
-import com.google.gerrit.common.errors.InvalidSshKeyException;
+import com.google.gerrit.exceptions.InvalidSshKeyException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.account.AccountSshKey;
 import com.google.inject.AbstractModule;
diff --git a/java/com/google/gerrit/server/ssh/SshKeyCreator.java b/java/com/google/gerrit/server/ssh/SshKeyCreator.java
index 55ba5ed..beaf1ba 100644
--- a/java/com/google/gerrit/server/ssh/SshKeyCreator.java
+++ b/java/com/google/gerrit/server/ssh/SshKeyCreator.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.ssh;
 
-import com.google.gerrit.common.errors.InvalidSshKeyException;
+import com.google.gerrit.exceptions.InvalidSshKeyException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.account.AccountSshKey;
 
diff --git a/java/com/google/gerrit/server/submit/ChangeSet.java b/java/com/google/gerrit/server/submit/ChangeSet.java
index 422e1b9..e721be41ab 100644
--- a/java/com/google/gerrit/server/submit/ChangeSet.java
+++ b/java/com/google/gerrit/server/submit/ChangeSet.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
 import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -77,7 +76,7 @@
     return changeData;
   }
 
-  public ListMultimap<Branch.NameKey, ChangeData> changesByBranch() throws OrmException {
+  public ListMultimap<Branch.NameKey, ChangeData> changesByBranch() {
     ListMultimap<Branch.NameKey, ChangeData> ret =
         MultimapBuilder.hashKeys().arrayListValues().build();
     for (ChangeData cd : changeData.values()) {
diff --git a/java/com/google/gerrit/server/submit/CherryPick.java b/java/com/google/gerrit/server/submit/CherryPick.java
index f50525c..8ff3cc5 100644
--- a/java/com/google/gerrit/server/submit/CherryPick.java
+++ b/java/com/google/gerrit/server/submit/CherryPick.java
@@ -30,7 +30,6 @@
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.RepoContext;
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -91,7 +90,7 @@
 
     @Override
     protected void updateRepoImpl(RepoContext ctx)
-        throws IntegrationException, IOException, OrmException, MethodNotAllowedException {
+        throws IntegrationException, IOException, MethodNotAllowedException {
       // If there is only one parent, a cherry-pick can be done by taking the
       // delta relative to that one parent and redoing that on the current merge
       // tip.
@@ -146,8 +145,7 @@
     }
 
     @Override
-    public PatchSet updateChangeImpl(ChangeContext ctx)
-        throws OrmException, NoSuchChangeException, IOException {
+    public PatchSet updateChangeImpl(ChangeContext ctx) throws NoSuchChangeException, IOException {
       if (newCommit == null && toMerge.getStatusCode() == SKIPPED_IDENTICAL_TREE) {
         return null;
       }
diff --git a/java/com/google/gerrit/server/submit/CommitMergeStatus.java b/java/com/google/gerrit/server/submit/CommitMergeStatus.java
index 5ac7c98..12172dd 100644
--- a/java/com/google/gerrit/server/submit/CommitMergeStatus.java
+++ b/java/com/google/gerrit/server/submit/CommitMergeStatus.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Provider;
 import java.util.List;
 import java.util.Optional;
@@ -93,8 +92,7 @@
       @Nullable CurrentUser caller,
       Provider<InternalChangeQuery> queryProvider,
       String commit,
-      String otherCommit)
-      throws OrmException {
+      String otherCommit) {
     List<ChangeData> changes = queryProvider.get().enforceVisibility(true).byCommit(otherCommit);
 
     if (changes.isEmpty()) {
diff --git a/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java b/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java
index 832c3879..831a8b8 100644
--- a/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java
+++ b/java/com/google/gerrit/server/submit/LocalMergeSuperSetComputation.java
@@ -23,6 +23,7 @@
 import com.google.common.collect.Iterables;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -39,7 +40,6 @@
 import com.google.gerrit.server.query.change.ChangeIsVisibleToPredicate;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
 import com.google.gerrit.server.submit.MergeOpRepoManager.OpenRepo;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -109,7 +109,7 @@
   @Override
   public ChangeSet completeWithoutTopic(
       MergeOpRepoManager orm, ChangeSet changeSet, CurrentUser user)
-      throws OrmException, IOException, PermissionBackendException {
+      throws IOException, PermissionBackendException {
     Collection<ChangeData> visibleChanges = new ArrayList<>();
     Collection<ChangeData> nonVisibleChanges = new ArrayList<>();
 
@@ -161,7 +161,7 @@
   }
 
   private static ImmutableListMultimap<Branch.NameKey, ChangeData> byBranch(
-      Iterable<ChangeData> changes) throws OrmException {
+      Iterable<ChangeData> changes) {
     ImmutableListMultimap.Builder<Branch.NameKey, ChangeData> builder =
         ImmutableListMultimap.builder();
     for (ChangeData cd : changes) {
@@ -202,7 +202,7 @@
     }
   }
 
-  private SubmitType submitType(ChangeData cd) throws OrmException {
+  private SubmitType submitType(ChangeData cd) {
     SubmitTypeRecord str = cd.submitTypeRecord();
     if (!str.isOk()) {
       logErrorAndThrow("Failed to get submit type for " + cd.getId() + ": " + str.errorMessage);
@@ -212,7 +212,7 @@
 
   private ChangeSet byCommitsOnBranchNotMerged(
       OpenRepo or, Branch.NameKey branch, Set<String> visibleHashes, Set<String> nonVisibleHashes)
-      throws OrmException, IOException {
+      throws IOException {
     List<ChangeData> potentiallyVisibleChanges =
         byCommitsOnBranchNotMerged(or, branch, visibleHashes);
     List<ChangeData> invisibleChanges =
@@ -229,7 +229,7 @@
   }
 
   private ImmutableList<ChangeData> byCommitsOnBranchNotMerged(
-      OpenRepo or, Branch.NameKey branch, Set<String> hashes) throws OrmException, IOException {
+      OpenRepo or, Branch.NameKey branch, Set<String> hashes) throws IOException {
     if (hashes.isEmpty()) {
       return ImmutableList.of();
     }
@@ -281,8 +281,8 @@
     }
   }
 
-  private void logErrorAndThrow(String msg) throws OrmException {
+  private void logErrorAndThrow(String msg) {
     logger.atSevere().log(msg);
-    throw new OrmException(msg);
+    throw new StorageException(msg);
   }
 }
diff --git a/java/com/google/gerrit/server/submit/MergeOp.java b/java/com/google/gerrit/server/submit/MergeOp.java
index 8b93ce8..5a38adb 100644
--- a/java/com/google/gerrit/server/submit/MergeOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOp.java
@@ -36,6 +36,7 @@
 import com.google.gerrit.common.data.SubmitRecord;
 import com.google.gerrit.common.data.SubmitRequirement;
 import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.api.changes.SubmitInput;
 import com.google.gerrit.extensions.client.SubmitType;
@@ -78,7 +79,6 @@
 import com.google.gerrit.server.update.RetryHelper.ActionType;
 import com.google.gerrit.server.update.UpdateException;
 import com.google.gerrit.server.util.time.TimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -124,7 +124,7 @@
     private final ListMultimap<Change.Id, String> problems;
     private final boolean allowClosed;
 
-    private CommitStatus(ChangeSet cs, boolean allowClosed) throws OrmException {
+    private CommitStatus(ChangeSet cs, boolean allowClosed) {
       checkArgument(
           !cs.furtherHiddenChanges(), "CommitStatus must not be called with hidden changes");
       changes = cs.changesById();
@@ -283,7 +283,7 @@
   }
 
   public static void checkSubmitRule(ChangeData cd, boolean allowClosed)
-      throws ResourceConflictException, OrmException {
+      throws ResourceConflictException {
     PatchSet patchSet = cd.currentPatchSet();
     if (patchSet == null) {
       throw new ResourceConflictException("missing current patch set for change " + cd.getId());
@@ -332,7 +332,7 @@
     return cd.submitRecords(submitRuleOptions(allowClosed));
   }
 
-  private static String describeNotReady(ChangeData cd, SubmitRecord record) throws OrmException {
+  private static String describeNotReady(ChangeData cd, SubmitRecord record) {
     List<String> blockingConditions = new ArrayList<>();
     if (record.labels != null) {
       blockingConditions.add(describeLabels(cd, record.labels));
@@ -345,8 +345,7 @@
     return Joiner.on("; ").join(blockingConditions);
   }
 
-  private static String describeLabels(ChangeData cd, List<SubmitRecord.Label> labels)
-      throws OrmException {
+  private static String describeLabels(ChangeData cd, List<SubmitRecord.Label> labels) {
     List<String> labelResults = new ArrayList<>();
     for (SubmitRecord.Label lbl : labels) {
       switch (lbl.status) {
@@ -394,7 +393,7 @@
         }
       } catch (ResourceConflictException e) {
         commitStatus.problem(cd.getId(), e.getMessage());
-      } catch (OrmException e) {
+      } catch (StorageException e) {
         String msg = "Error checking submit rules for change";
         logger.atWarning().withCause(e).log("%s %s", msg, cd.getId());
         commitStatus.problem(cd.getId(), msg);
@@ -426,7 +425,6 @@
    * @param caller the identity of the caller
    * @param checkSubmitRules whether the prolog submit rules should be evaluated
    * @param submitInput parameters regarding the merge
-   * @throws OrmException an error occurred reading or writing the database.
    * @throws RestApiException if an error occurred.
    * @throws PermissionBackendException if permissions can't be checked
    * @throws IOException an error occurred reading from NoteDb.
@@ -437,7 +435,7 @@
       boolean checkSubmitRules,
       SubmitInput submitInput,
       boolean dryrun)
-      throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException,
+      throws RestApiException, UpdateException, IOException, ConfigInvalidException,
           PermissionBackendException {
     this.submitInput = submitInput;
     this.notify =
@@ -520,7 +518,7 @@
         }
       } catch (IOException e) {
         // Anything before the merge attempt is an error
-        throw new OrmException(e);
+        throw new StorageException(e);
       }
     }
   }
@@ -580,7 +578,7 @@
     ListMultimap<Branch.NameKey, ChangeData> cbb;
     try {
       cbb = cs.changesByBranch();
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       throw new IntegrationException("Error reading changes to submit", e);
     }
     Set<Branch.NameKey> branches = cbb.keySet();
@@ -742,7 +740,7 @@
         notes = cd.notes();
         chg = cd.change();
         st = getSubmitType(cd);
-      } catch (OrmException e) {
+      } catch (StorageException e) {
         commitStatus.logProblem(changeId, e);
         continue;
       }
@@ -774,7 +772,7 @@
       Branch.NameKey destBranch = chg.getDest();
       try {
         ps = cd.currentPatchSet();
-      } catch (OrmException e) {
+      } catch (StorageException e) {
         commitStatus.logProblem(changeId, e);
         continue;
       }
@@ -870,7 +868,7 @@
         revisions.put(e.getValue().getObjectId(), PatchSet.Id.fromRef(e.getKey()));
       }
       return revisions;
-    } catch (IOException | OrmException e) {
+    } catch (IOException | StorageException e) {
       throw new IntegrationException("Failed to validate changes", e);
     }
   }
@@ -929,7 +927,7 @@
           }
         }
       }
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       logger.atWarning().withCause(e).log(
           "Cannot abandon changes for deleted project %s", destProject);
     }
diff --git a/java/com/google/gerrit/server/submit/MergeSorter.java b/java/com/google/gerrit/server/submit/MergeSorter.java
index dbdb0b4..f2f6537 100644
--- a/java/com/google/gerrit/server/submit/MergeSorter.java
+++ b/java/com/google/gerrit/server/submit/MergeSorter.java
@@ -19,7 +19,6 @@
 import com.google.gerrit.server.git.CodeReviewCommit;
 import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Provider;
 import java.io.IOException;
 import java.util.Collection;
@@ -54,7 +53,7 @@
   }
 
   public Collection<CodeReviewCommit> sort(Collection<CodeReviewCommit> toMerge)
-      throws IOException, OrmException {
+      throws IOException {
     final Set<CodeReviewCommit> heads = new HashSet<>();
     final Set<CodeReviewCommit> sort = new HashSet<>(toMerge);
     while (!sort.isEmpty()) {
diff --git a/java/com/google/gerrit/server/submit/MergeSuperSet.java b/java/com/google/gerrit/server/submit/MergeSuperSet.java
index 87ce6d4..d182f24 100644
--- a/java/com/google/gerrit/server/submit/MergeSuperSet.java
+++ b/java/com/google/gerrit/server/submit/MergeSuperSet.java
@@ -33,7 +33,6 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -93,7 +92,7 @@
   }
 
   public ChangeSet completeChangeSet(Change change, CurrentUser user)
-      throws IOException, OrmException, PermissionBackendException {
+      throws IOException, PermissionBackendException {
     try {
       if (orm == null) {
         orm = repoManagerProvider.get();
@@ -146,7 +145,7 @@
    */
   private ChangeSet topicClosure(
       ChangeSet changeSet, CurrentUser user, Set<String> topicsSeen, Set<String> visibleTopicsSeen)
-      throws OrmException, PermissionBackendException, IOException {
+      throws PermissionBackendException, IOException {
     List<ChangeData> visibleChanges = new ArrayList<>();
     List<ChangeData> nonVisibleChanges = new ArrayList<>();
 
@@ -181,7 +180,7 @@
   }
 
   private ChangeSet completeChangeSetIncludingTopics(ChangeSet changeSet, CurrentUser user)
-      throws IOException, OrmException, PermissionBackendException {
+      throws IOException, PermissionBackendException {
     Set<String> topicsSeen = new HashSet<>();
     Set<String> visibleTopicsSeen = new HashSet<>();
     int oldSeen;
@@ -201,7 +200,7 @@
     return changeSet;
   }
 
-  private List<ChangeData> byTopicOpen(String topic) throws OrmException {
+  private List<ChangeData> byTopicOpen(String topic) {
     return queryProvider.get().byTopicOpen(topic);
   }
 
diff --git a/java/com/google/gerrit/server/submit/MergeSuperSetComputation.java b/java/com/google/gerrit/server/submit/MergeSuperSetComputation.java
index a487c95..99239e3 100644
--- a/java/com/google/gerrit/server/submit/MergeSuperSetComputation.java
+++ b/java/com/google/gerrit/server/submit/MergeSuperSetComputation.java
@@ -17,7 +17,6 @@
 import com.google.gerrit.extensions.annotations.ExtensionPoint;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 
 /**
@@ -46,5 +45,5 @@
    * @return the completed set of changes that should be submitted together
    */
   ChangeSet completeWithoutTopic(MergeOpRepoManager orm, ChangeSet changeSet, CurrentUser user)
-      throws OrmException, IOException, PermissionBackendException;
+      throws IOException, PermissionBackendException;
 }
diff --git a/java/com/google/gerrit/server/submit/RebaseSorter.java b/java/com/google/gerrit/server/submit/RebaseSorter.java
index ccb08da..829ee9c 100644
--- a/java/com/google/gerrit/server/submit/RebaseSorter.java
+++ b/java/com/google/gerrit/server/submit/RebaseSorter.java
@@ -15,13 +15,13 @@
 package com.google.gerrit.server.submit;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.git.CodeReviewCommit;
 import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Provider;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -62,8 +62,7 @@
     this.incoming = incoming;
   }
 
-  public List<CodeReviewCommit> sort(Collection<CodeReviewCommit> toSort)
-      throws IOException, OrmException {
+  public List<CodeReviewCommit> sort(Collection<CodeReviewCommit> toSort) throws IOException {
     final List<CodeReviewCommit> sorted = new ArrayList<>();
     final Set<CodeReviewCommit> sort = new HashSet<>(toSort);
     while (!sort.isEmpty()) {
@@ -132,7 +131,7 @@
         }
       }
       return false;
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       throw new IOException(e);
     }
   }
diff --git a/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java b/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
index 8abef03..342ae69 100644
--- a/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
+++ b/java/com/google/gerrit/server/submit/RebaseSubmitStrategy.java
@@ -19,6 +19,7 @@
 import static com.google.gerrit.server.submit.CommitMergeStatus.SKIPPED_IDENTICAL_TREE;
 
 import com.google.common.collect.ImmutableList;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.restapi.MergeConflictException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestApiException;
@@ -34,7 +35,6 @@
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.Context;
 import com.google.gerrit.server.update.RepoContext;
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -59,7 +59,7 @@
     List<CodeReviewCommit> sorted;
     try {
       sorted = args.rebaseSorter.sort(toMerge);
-    } catch (IOException | OrmException e) {
+    } catch (IOException | StorageException e) {
       throw new IntegrationException("Commit sorting failed", e);
     }
     List<SubmitStrategyOp> ops = new ArrayList<>(sorted.size());
@@ -119,7 +119,7 @@
     @Override
     public void updateRepoImpl(RepoContext ctx)
         throws IntegrationException, InvalidChangeOperationException, RestApiException, IOException,
-            OrmException, PermissionBackendException {
+            PermissionBackendException {
       if (args.mergeUtil.canFastForward(
           args.mergeSorter, args.mergeTip.getCurrentTip(), args.rw, toMerge)) {
         if (!rebaseAlways) {
@@ -214,7 +214,7 @@
 
     @Override
     public PatchSet updateChangeImpl(ChangeContext ctx)
-        throws NoSuchChangeException, ResourceConflictException, OrmException, IOException {
+        throws NoSuchChangeException, ResourceConflictException, IOException {
       if (newCommit == null) {
         checkState(!rebaseAlways, "RebaseAlways must never fast forward");
         // otherwise, took the fast-forward option, nothing to do.
@@ -246,7 +246,7 @@
     }
 
     @Override
-    public void postUpdateImpl(Context ctx) throws OrmException {
+    public void postUpdateImpl(Context ctx) {
       if (rebaseOp != null) {
         rebaseOp.postUpdate(ctx);
       }
diff --git a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
index 1cc0db5..73bcc09 100644
--- a/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
+++ b/java/com/google/gerrit/server/submit/SubmitStrategyOp.java
@@ -23,6 +23,7 @@
 import com.google.common.base.Function;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
@@ -48,7 +49,6 @@
 import com.google.gerrit.server.update.ChangeContext;
 import com.google.gerrit.server.update.Context;
 import com.google.gerrit.server.update.RepoContext;
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -283,7 +283,7 @@
             : alreadyMergedCommit;
     try {
       setMerged(ctx, message(ctx, commit, s));
-    } catch (OrmException err) {
+    } catch (StorageException err) {
       String msg = "Error updating change status for " + id;
       logger.atSevere().withCause(err).log(msg);
       args.commitStatus.logProblem(id, msg);
@@ -294,8 +294,7 @@
     return true;
   }
 
-  private PatchSet getOrCreateAlreadyMergedPatchSet(ChangeContext ctx)
-      throws IOException, OrmException {
+  private PatchSet getOrCreateAlreadyMergedPatchSet(ChangeContext ctx) throws IOException {
     PatchSet.Id psId = alreadyMergedCommit.getPatchsetId();
     logger.atFine().log("Fixing up already-merged patch set %s", psId);
     PatchSet prevPs = args.psUtil.current(ctx.getNotes());
@@ -317,8 +316,7 @@
         ctx.getRevWalk(), ctx.getUpdate(psId), psId, alreadyMergedCommit, groups, null, null);
   }
 
-  private void setApproval(ChangeContext ctx, IdentifiedUser user)
-      throws OrmException, IOException {
+  private void setApproval(ChangeContext ctx, IdentifiedUser user) throws IOException {
     Change.Id id = ctx.getChange().getId();
     List<SubmitRecord> records = args.commitStatus.getSubmitRecords(id);
     PatchSet.Id oldPsId = toMerge.getPatchsetId();
@@ -340,7 +338,7 @@
   }
 
   private LabelNormalizer.Result approve(ChangeContext ctx, ChangeUpdate update)
-      throws OrmException, IOException {
+      throws IOException {
     PatchSet.Id psId = update.getPatchSetId();
     Map<PatchSetApproval.Key, PatchSetApproval> byKey = new HashMap<>();
     for (PatchSetApproval psa :
@@ -404,8 +402,7 @@
     return "";
   }
 
-  private ChangeMessage message(ChangeContext ctx, CodeReviewCommit commit, CommitMergeStatus s)
-      throws OrmException {
+  private ChangeMessage message(ChangeContext ctx, CodeReviewCommit commit, CommitMergeStatus s) {
     requireNonNull(s, "CommitMergeStatus may not be null");
     String txt = s.getDescription();
     if (s == CommitMergeStatus.CLEAN_MERGE) {
diff --git a/java/com/google/gerrit/server/update/BatchUpdate.java b/java/com/google/gerrit/server/update/BatchUpdate.java
index 27a6aee..9f41f26 100644
--- a/java/com/google/gerrit/server/update/BatchUpdate.java
+++ b/java/com/google/gerrit/server/update/BatchUpdate.java
@@ -29,6 +29,8 @@
 import com.google.common.collect.MultimapBuilder;
 import com.google.common.collect.Multiset;
 import com.google.common.flogger.FluentLogger;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.api.changes.NotifyHandling;
 import com.google.gerrit.extensions.config.FactoryModule;
@@ -54,7 +56,6 @@
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.NoSuchProjectException;
 import com.google.gerrit.server.project.NoSuchRefException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Module;
 import com.google.inject.assistedinject.Assisted;
@@ -125,9 +126,7 @@
     checkDifferentProject(updates);
 
     try {
-      @SuppressWarnings("deprecation")
-      List<com.google.common.util.concurrent.CheckedFuture<?, IOException>> indexFutures =
-          new ArrayList<>();
+      List<ListenableFuture<?>> indexFutures = new ArrayList<>();
       List<ChangesHandle> handles = new ArrayList<>(updates.size());
       try {
         for (BatchUpdate u : updates) {
@@ -149,7 +148,7 @@
         }
       }
 
-      ChangeIndexer.allAsList(indexFutures).get();
+      ((ListenableFuture<?>) Futures.allAsList(indexFutures)).get();
 
       // Fire ref update events only after all mutations are finished, since callers may assume a
       // patch set ref being created means the change was created, or a branch advancing meaning
@@ -500,18 +499,17 @@
       checkArgument(old == null, "result for change %s already set: %s", id, old);
     }
 
-    void execute() throws OrmException, IOException {
+    void execute() throws IOException {
       BatchUpdate.this.batchRefUpdate = manager.execute(dryrun);
     }
 
     @SuppressWarnings("deprecation")
-    List<com.google.common.util.concurrent.CheckedFuture<?, IOException>> startIndexFutures() {
+    List<ListenableFuture<?>> startIndexFutures() {
       if (dryrun) {
         return ImmutableList.of();
       }
       logDebug("Reindexing %d changes", results.size());
-      List<com.google.common.util.concurrent.CheckedFuture<?, IOException>> indexFutures =
-          new ArrayList<>(results.size());
+      List<ListenableFuture<?>> indexFutures = new ArrayList<>(results.size());
       for (Map.Entry<Change.Id, ChangeResult> e : results.entrySet()) {
         Change.Id id = e.getKey();
         switch (e.getValue()) {
@@ -583,7 +581,7 @@
     return handle;
   }
 
-  private ChangeContextImpl newChangeContext(Change.Id id) throws OrmException {
+  private ChangeContextImpl newChangeContext(Change.Id id) {
     logDebug("Opening change %s for update", id);
     Change c = newChanges.get(id);
     boolean isNew = c != null;
diff --git a/java/com/google/gerrit/server/util/MostSpecificComparator.java b/java/com/google/gerrit/server/util/MostSpecificComparator.java
index f243726..b22617c 100644
--- a/java/com/google/gerrit/server/util/MostSpecificComparator.java
+++ b/java/com/google/gerrit/server/util/MostSpecificComparator.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.util;
 
-import com.google.gerrit.common.data.RefConfigSection;
+import com.google.gerrit.common.data.AccessSection;
 import com.google.gerrit.server.project.RefPattern;
 import java.util.Comparator;
 import org.apache.commons.lang.StringUtils;
@@ -40,7 +40,7 @@
  * are infinite, but refs/heads/[a-zA-Z]* has more transitions, which after all turns it more
  * specific.
  */
-public final class MostSpecificComparator implements Comparator<RefConfigSection> {
+public final class MostSpecificComparator implements Comparator<AccessSection> {
   private final String refName;
 
   public MostSpecificComparator(String refName) {
@@ -48,7 +48,7 @@
   }
 
   @Override
-  public int compare(RefConfigSection a, RefConfigSection b) {
+  public int compare(AccessSection a, AccessSection b) {
     return compare(a.getName(), b.getName());
   }
 
diff --git a/java/com/google/gerrit/server/util/ThreadLocalRequestContext.java b/java/com/google/gerrit/server/util/ThreadLocalRequestContext.java
index 06e93f8..afd699c 100644
--- a/java/com/google/gerrit/server/util/ThreadLocalRequestContext.java
+++ b/java/com/google/gerrit/server/util/ThreadLocalRequestContext.java
@@ -16,7 +16,7 @@
 
 import com.google.common.base.MoreObjects;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.errors.NotSignedInException;
+import com.google.gerrit.exceptions.NotSignedInException;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.inject.AbstractModule;
diff --git a/java/com/google/gerrit/sshd/AbstractGitCommand.java b/java/com/google/gerrit/sshd/AbstractGitCommand.java
index d726ef6..c49ae82 100644
--- a/java/com/google/gerrit/sshd/AbstractGitCommand.java
+++ b/java/com/google/gerrit/sshd/AbstractGitCommand.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.sshd.SshScope.Context;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import org.apache.sshd.server.Environment;
@@ -85,7 +84,7 @@
     return n;
   }
 
-  private void service() throws IOException, OrmException, PermissionBackendException, Failure {
+  private void service() throws IOException, PermissionBackendException, Failure {
     project = projectState.getProject();
     projectName = project.getNameKey();
 
@@ -102,6 +101,5 @@
     }
   }
 
-  protected abstract void runImpl()
-      throws IOException, OrmException, PermissionBackendException, Failure;
+  protected abstract void runImpl() throws IOException, PermissionBackendException, Failure;
 }
diff --git a/java/com/google/gerrit/sshd/BUILD b/java/com/google/gerrit/sshd/BUILD
index ac73482..42ef7d5 100644
--- a/java/com/google/gerrit/sshd/BUILD
+++ b/java/com/google/gerrit/sshd/BUILD
@@ -5,6 +5,7 @@
     deps = [
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/json",
         "//java/com/google/gerrit/lifecycle",
@@ -24,7 +25,6 @@
         "//lib:args4j",
         "//lib:gson",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:jsch",
         "//lib:servlet-api-3_1",
         "//lib/auto:auto-value",
diff --git a/java/com/google/gerrit/sshd/ChangeArgumentParser.java b/java/com/google/gerrit/sshd/ChangeArgumentParser.java
index c8586f2..67df1c6 100644
--- a/java/com/google/gerrit/sshd/ChangeArgumentParser.java
+++ b/java/com/google/gerrit/sshd/ChangeArgumentParser.java
@@ -28,7 +28,6 @@
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.server.restapi.change.ChangesCollection;
 import com.google.gerrit.sshd.BaseCommand.UnloggedFailure;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -55,13 +54,13 @@
   }
 
   public void addChange(String id, Map<Change.Id, ChangeResource> changes)
-      throws UnloggedFailure, OrmException, PermissionBackendException, IOException {
+      throws UnloggedFailure, PermissionBackendException, IOException {
     addChange(id, changes, null);
   }
 
   public void addChange(
       String id, Map<Change.Id, ChangeResource> changes, ProjectState projectState)
-      throws UnloggedFailure, OrmException, PermissionBackendException, IOException {
+      throws UnloggedFailure, PermissionBackendException, IOException {
     addChange(id, changes, projectState, true);
   }
 
@@ -70,7 +69,7 @@
       Map<Change.Id, ChangeResource> changes,
       ProjectState projectState,
       boolean useIndex)
-      throws UnloggedFailure, OrmException, PermissionBackendException, IOException {
+      throws UnloggedFailure, PermissionBackendException, IOException {
     List<ChangeNotes> matched = useIndex ? changeFinder.find(id) : changeFromNotesFactory(id);
     List<ChangeNotes> toAdd = new ArrayList<>(changes.size());
     boolean canMaintainServer;
@@ -116,7 +115,7 @@
     changes.put(cId, changeResource);
   }
 
-  private List<ChangeNotes> changeFromNotesFactory(String id) throws OrmException, UnloggedFailure {
+  private List<ChangeNotes> changeFromNotesFactory(String id) throws UnloggedFailure {
     return changeNotesFactory.create(parseId(id));
   }
 
diff --git a/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java b/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java
index d89f9e0..da0ec1d 100644
--- a/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java
+++ b/java/com/google/gerrit/sshd/SshKeyCreatorImpl.java
@@ -15,7 +15,7 @@
 package com.google.gerrit.sshd;
 
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.errors.InvalidSshKeyException;
+import com.google.gerrit.exceptions.InvalidSshKeyException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.server.account.AccountSshKey;
 import com.google.gerrit.server.ssh.SshKeyCreator;
diff --git a/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java b/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
index 8a37cce..8875f07 100644
--- a/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateAccountCommand.java
@@ -28,7 +28,6 @@
 import com.google.gerrit.server.restapi.account.CreateAccount;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -72,8 +71,7 @@
 
   @Override
   protected void run()
-      throws OrmException, IOException, ConfigInvalidException, UnloggedFailure,
-          PermissionBackendException {
+      throws IOException, ConfigInvalidException, UnloggedFailure, PermissionBackendException {
     AccountInput input = new AccountInput();
     input.username = username;
     input.email = email;
diff --git a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
index 917c138..f9a04a0 100644
--- a/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
+++ b/java/com/google/gerrit/sshd/commands/CreateGroupCommand.java
@@ -33,7 +33,6 @@
 import com.google.gerrit.server.restapi.group.GroupsCollection;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.HashSet;
@@ -102,8 +101,7 @@
 
   @Override
   protected void run()
-      throws Failure, OrmException, IOException, ConfigInvalidException,
-          PermissionBackendException {
+      throws Failure, IOException, ConfigInvalidException, PermissionBackendException {
     try {
       GroupResource rsrc = createGroup();
 
@@ -120,8 +118,7 @@
   }
 
   private GroupResource createGroup()
-      throws RestApiException, OrmException, IOException, ConfigInvalidException,
-          PermissionBackendException {
+      throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     GroupInput input = new GroupInput();
     input.description = groupDescription;
     input.visibleToAll = visibleToAll;
@@ -136,8 +133,7 @@
   }
 
   private void addMembers(GroupResource rsrc)
-      throws RestApiException, OrmException, IOException, ConfigInvalidException,
-          PermissionBackendException {
+      throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     AddMembers.Input input =
         AddMembers.Input.fromMembers(
             initialMembers.stream().map(Object::toString).collect(toList()));
@@ -145,8 +141,7 @@
   }
 
   private void addSubgroups(GroupResource rsrc)
-      throws RestApiException, OrmException, IOException, ConfigInvalidException,
-          PermissionBackendException {
+      throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     AddSubgroups.Input input =
         AddSubgroups.Input.fromGroups(
             initialGroups.stream().map(AccountGroup.UUID::get).collect(toList()));
diff --git a/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java b/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
index fad74f5..5aa2ec8 100644
--- a/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
+++ b/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.sshd.commands;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.common.Input;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.change.ChangeResource;
@@ -22,7 +23,6 @@
 import com.google.gerrit.sshd.ChangeArgumentParser;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.LinkedHashMap;
@@ -44,7 +44,7 @@
   void addChange(String token) {
     try {
       changeArgumentParser.addChange(token, changes, null, false);
-    } catch (UnloggedFailure | OrmException | PermissionBackendException | IOException e) {
+    } catch (UnloggedFailure | StorageException | PermissionBackendException | IOException e) {
       writeError("warning", e.getMessage());
     }
   }
diff --git a/java/com/google/gerrit/sshd/commands/LsUserRefs.java b/java/com/google/gerrit/sshd/commands/LsUserRefs.java
index 3d9ef565..1f991e0 100644
--- a/java/com/google/gerrit/sshd/commands/LsUserRefs.java
+++ b/java/com/google/gerrit/sshd/commands/LsUserRefs.java
@@ -17,6 +17,7 @@
 import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
 
 import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.reviewdb.client.Account;
@@ -32,7 +33,6 @@
 import com.google.gerrit.server.util.OneOffRequestContext;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.Map;
@@ -81,7 +81,7 @@
       stdout.println(e.getMessage());
       stdout.flush();
       return;
-    } catch (OrmException | IOException | ConfigInvalidException e) {
+    } catch (StorageException | IOException | ConfigInvalidException e) {
       throw die(e);
     }
 
diff --git a/java/com/google/gerrit/sshd/commands/PatchSetParser.java b/java/com/google/gerrit/sshd/commands/PatchSetParser.java
index d174561..a6e802a 100644
--- a/java/com/google/gerrit/sshd/commands/PatchSetParser.java
+++ b/java/com/google/gerrit/sshd/commands/PatchSetParser.java
@@ -27,7 +27,6 @@
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.InternalChangeQuery;
 import com.google.gerrit.sshd.BaseCommand.UnloggedFailure;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
@@ -54,7 +53,7 @@
   }
 
   public PatchSet parsePatchSet(String token, ProjectState projectState, String branch)
-      throws UnloggedFailure, OrmException {
+      throws UnloggedFailure {
     // By commit?
     //
     if (token.matches("^([0-9a-fA-F]{4," + RevId.LEN + "})$")) {
@@ -123,7 +122,7 @@
   }
 
   private ChangeNotes getNotes(@Nullable ProjectState projectState, Change.Id changeId)
-      throws OrmException, UnloggedFailure {
+      throws UnloggedFailure {
     if (projectState != null) {
       return notesFactory.create(projectState.getNameKey(), changeId);
     }
diff --git a/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index 0861540..7af07de 100644
--- a/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -23,6 +23,7 @@
 import com.google.common.io.CharStreams;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelValue;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.GerritApi;
 import com.google.gerrit.extensions.api.changes.AbandonInput;
 import com.google.gerrit.extensions.api.changes.ChangeApi;
@@ -44,7 +45,6 @@
 import com.google.gerrit.util.cli.CmdLineParser;
 import com.google.gerrit.util.cli.OptionUtil;
 import com.google.gson.JsonSyntaxException;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.io.InputStreamReader;
@@ -89,7 +89,7 @@
       patchSets.add(ps);
     } catch (UnloggedFailure e) {
       throw new IllegalArgumentException(e.getMessage(), e);
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       throw new IllegalArgumentException("database error", e);
     }
   }
diff --git a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index 9bcb103..e4ea40d 100644
--- a/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -18,7 +18,7 @@
 
 import com.google.common.base.Strings;
 import com.google.gerrit.common.RawInputUtil;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.accounts.EmailInput;
 import com.google.gerrit.extensions.api.accounts.SshKeyInput;
 import com.google.gerrit.extensions.common.EmailInfo;
@@ -52,7 +52,6 @@
 import com.google.gerrit.server.restapi.account.PutPreferred;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.BufferedReader;
@@ -213,8 +212,7 @@
   }
 
   private void setAccount()
-      throws OrmException, IOException, UnloggedFailure, ConfigInvalidException,
-          PermissionBackendException {
+      throws IOException, UnloggedFailure, ConfigInvalidException, PermissionBackendException {
     user = genericUserFactory.create(id);
     rsrc = new AccountResource(user.asIdentifiedUser());
     try {
@@ -273,8 +271,7 @@
   }
 
   private void addSshKeys(List<String> sshKeys)
-      throws RestApiException, OrmException, IOException, ConfigInvalidException,
-          PermissionBackendException {
+      throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     for (String sshKey : sshKeys) {
       SshKeyInput in = new SshKeyInput();
       in.raw = RawInputUtil.create(sshKey.getBytes(UTF_8), "plain/text");
@@ -283,8 +280,8 @@
   }
 
   private void deleteSshKeys(List<String> sshKeys)
-      throws RestApiException, OrmException, RepositoryNotFoundException, IOException,
-          ConfigInvalidException, PermissionBackendException {
+      throws RestApiException, RepositoryNotFoundException, IOException, ConfigInvalidException,
+          PermissionBackendException {
     List<SshKeyInfo> infos = getSshKeys.apply(rsrc);
     if (sshKeys.contains("ALL")) {
       for (SshKeyInfo i : infos) {
@@ -302,14 +299,14 @@
   }
 
   private void deleteSshKey(SshKeyInfo i)
-      throws AuthException, OrmException, RepositoryNotFoundException, IOException,
-          ConfigInvalidException, PermissionBackendException {
+      throws AuthException, RepositoryNotFoundException, IOException, ConfigInvalidException,
+          PermissionBackendException {
     AccountSshKey sshKey = AccountSshKey.create(user.getAccountId(), i.seq, i.sshPublicKey);
     deleteSshKey.apply(new AccountResource.SshKey(user.asIdentifiedUser(), sshKey), null);
   }
 
   private void addEmail(String email)
-      throws UnloggedFailure, RestApiException, OrmException, IOException, ConfigInvalidException,
+      throws UnloggedFailure, RestApiException, IOException, ConfigInvalidException,
           PermissionBackendException {
     EmailInput in = new EmailInput();
     in.email = email;
@@ -322,8 +319,7 @@
   }
 
   private void deleteEmail(String email)
-      throws RestApiException, OrmException, IOException, ConfigInvalidException,
-          PermissionBackendException {
+      throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
     if (email.equals("ALL")) {
       List<EmailInfo> emails = getEmails.apply(rsrc);
       for (EmailInfo e : emails) {
@@ -335,8 +331,7 @@
   }
 
   private void putPreferred(String email)
-      throws RestApiException, OrmException, IOException, PermissionBackendException,
-          ConfigInvalidException {
+      throws RestApiException, IOException, PermissionBackendException, ConfigInvalidException {
     for (EmailInfo e : getEmails.apply(rsrc)) {
       if (e.email.equals(email)) {
         putPreferred.apply(new AccountResource.Email(user.asIdentifiedUser(), email), null);
diff --git a/java/com/google/gerrit/sshd/commands/SetParentCommand.java b/java/com/google/gerrit/sshd/commands/SetParentCommand.java
index f2d8c4c..dfdf7f2 100644
--- a/java/com/google/gerrit/sshd/commands/SetParentCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetParentCommand.java
@@ -16,6 +16,7 @@
 
 import static java.util.stream.Collectors.toList;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.projects.ParentInput;
 import com.google.gerrit.extensions.common.ProjectInfo;
 import com.google.gerrit.extensions.restapi.AuthException;
@@ -33,7 +34,6 @@
 import com.google.gerrit.server.restapi.project.SetParent;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -112,7 +112,7 @@
         childProjects.addAll(getChildrenForReparenting(oldParent));
       } catch (PermissionBackendException e) {
         throw new Failure(1, "permissions unavailable", e);
-      } catch (OrmException | RestApiException e) {
+      } catch (StorageException | RestApiException e) {
         throw new Failure(1, "failure in request", e);
       }
     }
@@ -149,7 +149,7 @@
    * reparenting.
    */
   private List<Project.NameKey> getChildrenForReparenting(ProjectState parent)
-      throws PermissionBackendException, OrmException, RestApiException {
+      throws PermissionBackendException, RestApiException {
     final List<Project.NameKey> childProjects = new ArrayList<>();
     final List<Project.NameKey> excluded = new ArrayList<>(excludedChildren.size());
     for (ProjectState excludedChild : excludedChildren) {
diff --git a/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java b/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
index a4a8ea8..30caa43 100644
--- a/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
+++ b/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.sshd.commands;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
 import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -29,7 +30,6 @@
 import com.google.gerrit.sshd.ChangeArgumentParser;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -75,7 +75,7 @@
       changeArgumentParser.addChange(token, changes, projectState);
     } catch (IOException | UnloggedFailure e) {
       throw new IllegalArgumentException(e.getMessage(), e);
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       throw new IllegalArgumentException("database is down", e);
     } catch (PermissionBackendException e) {
       throw new IllegalArgumentException("can't check permissions", e);
diff --git a/java/com/google/gerrit/sshd/commands/UploadArchive.java b/java/com/google/gerrit/sshd/commands/UploadArchive.java
index 26a0d15..24f82a7 100644
--- a/java/com/google/gerrit/sshd/commands/UploadArchive.java
+++ b/java/com/google/gerrit/sshd/commands/UploadArchive.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.restapi.change.AllowedFormats;
 import com.google.gerrit.server.restapi.project.CommitsCollection;
 import com.google.gerrit.sshd.AbstractGitCommand;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -165,7 +164,7 @@
   }
 
   @Override
-  protected void runImpl() throws IOException, OrmException, PermissionBackendException, Failure {
+  protected void runImpl() throws IOException, PermissionBackendException, Failure {
     PacketLineOut packetOut = new PacketLineOut(out);
     packetOut.setFlushOnEnd(true);
     packetOut.writeString("ACK");
@@ -245,8 +244,7 @@
     return Collections.emptyMap();
   }
 
-  private boolean canRead(ObjectId revId)
-      throws IOException, OrmException, PermissionBackendException {
+  private boolean canRead(ObjectId revId) throws IOException, PermissionBackendException {
     ProjectState projectState = projectCache.get(projectName);
     requireNonNull(projectState, () -> String.format("Failed to load project %s", projectName));
 
diff --git a/java/com/google/gerrit/testing/BUILD b/java/com/google/gerrit/testing/BUILD
index 71efda6..ec5076e 100644
--- a/java/com/google/gerrit/testing/BUILD
+++ b/java/com/google/gerrit/testing/BUILD
@@ -14,6 +14,7 @@
     deps = [
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/gpg",
         "//java/com/google/gerrit/index",
@@ -34,7 +35,6 @@
         "//java/com/google/gerrit/server/schema",
         "//java/com/google/gerrit/server/util/time",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:h2",
         "//lib:junit",
         "//lib/auto:auto-value",
diff --git a/java/com/google/gerrit/testing/FakeEmailSender.java b/java/com/google/gerrit/testing/FakeEmailSender.java
index 4977cb3..a60995b 100644
--- a/java/com/google/gerrit/testing/FakeEmailSender.java
+++ b/java/com/google/gerrit/testing/FakeEmailSender.java
@@ -21,7 +21,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
-import com.google.gerrit.common.errors.EmailException;
+import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.mail.EmailHeader;
 import com.google.gerrit.mail.MailHeader;
diff --git a/java/com/google/gwtorm/BUILD b/java/com/google/gwtorm/BUILD
new file mode 100644
index 0000000..e9cac34
--- /dev/null
+++ b/java/com/google/gwtorm/BUILD
@@ -0,0 +1,5 @@
+java_library(
+    name = "gwtorm",
+    srcs = glob(["**/*.java"]),
+    visibility = ["//visibility:public"],
+)
diff --git a/java/com/google/gwtorm/client/CompoundKey.java b/java/com/google/gwtorm/client/CompoundKey.java
new file mode 100644
index 0000000..1c66d18
--- /dev/null
+++ b/java/com/google/gwtorm/client/CompoundKey.java
@@ -0,0 +1,108 @@
+// Copyright 2008 Google Inc.
+//
+// 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.gwtorm.client;
+
+import java.io.Serializable;
+
+/**
+ * Abstract key type composed of other keys.
+ *
+ * <p>Applications should subclass this type to create their own entity-specific key classes.
+ *
+ * @param <P> the parent key type. Use {@link Key} if no parent key is needed.
+ */
+@SuppressWarnings("serial")
+public abstract class CompoundKey<P extends Key<?>> implements Key<P>, Serializable {
+  /** @return the member key components, minus the parent key. */
+  public abstract Key<?>[] members();
+
+  /** @return the parent key instance; null if this is a root level key. */
+  @Override
+  public P getParentKey() {
+    return null;
+  }
+
+  @Override
+  public int hashCode() {
+    int hc = 0;
+    if (getParentKey() != null) {
+      hc = getParentKey().hashCode();
+    }
+    for (final Key<?> k : members()) {
+      hc *= 31;
+      hc += k.hashCode();
+    }
+    return hc;
+  }
+
+  @Override
+  public boolean equals(final Object b) {
+    if (b == null || b.getClass() != getClass()) {
+      return false;
+    }
+
+    final CompoundKey<P> q = cast(b);
+    if (getParentKey() != null && !getParentKey().equals(q.getParentKey())) {
+      return false;
+    }
+
+    final Key<?>[] aMembers = members();
+    final Key<?>[] bMembers = q.members();
+    if (aMembers.length != bMembers.length) {
+      return false;
+    }
+    for (int i = 0; i < aMembers.length; i++) {
+      if (!aMembers[i].equals(bMembers[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public String toString() {
+    final StringBuffer r = new StringBuffer();
+    boolean first = true;
+    if (getParentKey() != null) {
+      r.append(KeyUtil.encode(getParentKey().toString()));
+      first = false;
+    }
+    for (final Key<?> k : members()) {
+      if (!first) {
+        r.append(',');
+      }
+      r.append(KeyUtil.encode(k.toString()));
+      first = false;
+    }
+    return r.toString();
+  }
+
+  @Override
+  public void fromString(final String in) {
+    final String[] parts = in.split(",");
+    int p = 0;
+    if (getParentKey() != null) {
+      getParentKey().fromString(KeyUtil.decode(parts[p++]));
+    }
+    for (final Key<?> k : members()) {
+      k.fromString(KeyUtil.decode(parts[p++]));
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private static <A extends Key<?>> CompoundKey<A> cast(final Object b) {
+    return (CompoundKey<A>) b;
+  }
+}
diff --git a/java/com/google/gwtorm/client/IntKey.java b/java/com/google/gwtorm/client/IntKey.java
new file mode 100644
index 0000000..08c90e0
--- /dev/null
+++ b/java/com/google/gwtorm/client/IntKey.java
@@ -0,0 +1,80 @@
+// Copyright 2008 Google Inc.
+//
+// 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.gwtorm.client;
+
+import java.io.Serializable;
+
+/**
+ * Abstract key type using a single integer value.
+ *
+ * <p>Applications should subclass this type to create their own entity-specific key classes.
+ *
+ * @param <P> the parent key type. Use {@link Key} if no parent key is needed.
+ */
+@SuppressWarnings("serial")
+public abstract class IntKey<P extends Key<?>> implements Key<P>, Serializable {
+  /** @return id of the entity instance. */
+  public abstract int get();
+
+  /** @param newValue the new value of this key. */
+  protected abstract void set(int newValue);
+
+  /** @return the parent key instance; null if this is a root level key. */
+  @Override
+  public P getParentKey() {
+    return null;
+  }
+
+  @Override
+  public int hashCode() {
+    int hc = get();
+    if (getParentKey() != null) {
+      hc *= 31;
+      hc += getParentKey().hashCode();
+    }
+    return hc;
+  }
+
+  @Override
+  public boolean equals(final Object b) {
+    if (b == null || b.getClass() != getClass()) {
+      return false;
+    }
+
+    final IntKey<P> q = cast(b);
+    return get() == q.get() && KeyUtil.eq(getParentKey(), q.getParentKey());
+  }
+
+  @Override
+  public String toString() {
+    final StringBuffer r = new StringBuffer();
+    if (getParentKey() != null) {
+      r.append(getParentKey().toString());
+      r.append(',');
+    }
+    r.append(get());
+    return r.toString();
+  }
+
+  @Override
+  public void fromString(final String in) {
+    set(Integer.parseInt(KeyUtil.parseFromString(getParentKey(), in)));
+  }
+
+  @SuppressWarnings("unchecked")
+  private static <A extends Key<?>> IntKey<A> cast(final Object b) {
+    return (IntKey<A>) b;
+  }
+}
diff --git a/java/com/google/gwtorm/client/Key.java b/java/com/google/gwtorm/client/Key.java
new file mode 100644
index 0000000..69a2248
--- /dev/null
+++ b/java/com/google/gwtorm/client/Key.java
@@ -0,0 +1,45 @@
+// Copyright 2008 Google Inc.
+//
+// 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.gwtorm.client;
+
+/**
+ * Generic type for an entity key.
+ *
+ * <p>Although not required, entities should make their primary key type implement this interface,
+ * permitting traversal up through the containment hierarchy of the entity keys.
+ *
+ * @param <P> type of the parent key. If no parent, use {@link Key} itself.
+ */
+public interface Key<P extends Key<?>> {
+  /**
+   * Get the parent key instance.
+   *
+   * @return the parent key; null if this entity key is a root-level key.
+   */
+  public P getParentKey();
+
+  @Override
+  public int hashCode();
+
+  @Override
+  public boolean equals(Object o);
+
+  /** @return the key, encoded in a string format . */
+  @Override
+  public String toString();
+
+  /** Reset this key instance to represent the data in the supplied string. */
+  public void fromString(String in);
+}
diff --git a/java/com/google/gwtorm/client/KeyUtil.java b/java/com/google/gwtorm/client/KeyUtil.java
new file mode 100644
index 0000000..e236d37
--- /dev/null
+++ b/java/com/google/gwtorm/client/KeyUtil.java
@@ -0,0 +1,91 @@
+// Copyright 2008 Google Inc.
+//
+// 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.gwtorm.client;
+
+/** Common utility functions for {@link Key} implementors. */
+public class KeyUtil {
+  private static Encoder ENCODER_IMPL = new StandardKeyEncoder();
+
+  /**
+   * Determine if two keys are equal, supporting null references.
+   *
+   * @param <T> type of the key entity.
+   * @param a first key to test; may be null.
+   * @param b second key to test; may be null.
+   * @return true if both <code>a</code> and <code>b</code> are null, or if both are not-null and
+   *     <code>a.equals(b)</code> is true. Otherwise false.
+   */
+  public static <T extends Key<?>> boolean eq(final T a, final T b) {
+    if (a == b) {
+      return true;
+    }
+    if (a == null || b == null) {
+      return false;
+    }
+    return a.equals(b);
+  }
+
+  /**
+   * Encode a string to be safe for use within a URL like string.
+   *
+   * <p>The returned encoded string has URL component characters escaped with hex escapes (e.g. ' '
+   * is '+' and '%' is '%25'). The special character '/' is left literal. The comma character (',')
+   * is always encoded, permitting multiple encoded string values to be joined together safely.
+   *
+   * @param e the string to encode, must not be null.
+   * @return the encoded string.
+   */
+  public static String encode(final String e) {
+    return ENCODER_IMPL.encode(e);
+  }
+
+  /**
+   * Decode a string previously encoded by {@link #encode(String)}.
+   *
+   * @param e the string to decode, must not be null.
+   * @return the decoded string.
+   */
+  public static String decode(final String e) {
+    return ENCODER_IMPL.decode(e);
+  }
+
+  /**
+   * Split a string along the last comma and parse into the parent.
+   *
+   * @param parent parent key; <code>parent.fromString(in[0..comma])</code>.
+   * @param in the input string.
+   * @return text (if any) after the last comma in the input.
+   */
+  public static String parseFromString(final Key<?> parent, final String in) {
+    final int comma = in.lastIndexOf(',');
+    if (comma < 0 && parent == null) {
+      return decode(in);
+    }
+    if (comma < 0 && parent != null) {
+      throw new IllegalArgumentException("Not enough components: " + in);
+    }
+    assert (parent != null);
+    parent.fromString(in.substring(0, comma));
+    return decode(in.substring(comma + 1));
+  }
+
+  public abstract static class Encoder {
+    public abstract String encode(String e);
+
+    public abstract String decode(String e);
+  }
+
+  private KeyUtil() {}
+}
diff --git a/java/com/google/gwtorm/client/StandardKeyEncoder.java b/java/com/google/gwtorm/client/StandardKeyEncoder.java
new file mode 100644
index 0000000..d6d503a
--- /dev/null
+++ b/java/com/google/gwtorm/client/StandardKeyEncoder.java
@@ -0,0 +1,111 @@
+// Copyright 2008 Google Inc.
+//
+// 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.gwtorm.client;
+
+import com.google.gwtorm.client.KeyUtil.Encoder;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+public class StandardKeyEncoder extends Encoder {
+  private static final char[] hexc = {
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+  };
+  private static final char safe[];
+  private static final byte hexb[];
+
+  static {
+    safe = new char[256];
+    safe['-'] = '-';
+    safe['_'] = '_';
+    safe['.'] = '.';
+    safe['!'] = '!';
+    safe['~'] = '~';
+    safe['*'] = '*';
+    safe['\''] = '\'';
+    safe['('] = '(';
+    safe[')'] = ')';
+    safe['/'] = '/';
+    safe[' '] = '+';
+    for (char c = '0'; c <= '9'; c++) safe[c] = c;
+    for (char c = 'A'; c <= 'Z'; c++) safe[c] = c;
+    for (char c = 'a'; c <= 'z'; c++) safe[c] = c;
+
+    hexb = new byte['f' + 1];
+    Arrays.fill(hexb, (byte) -1);
+    for (char i = '0'; i <= '9'; i++) hexb[i] = (byte) (i - '0');
+    for (char i = 'A'; i <= 'F'; i++) hexb[i] = (byte) ((i - 'A') + 10);
+    for (char i = 'a'; i <= 'f'; i++) hexb[i] = (byte) ((i - 'a') + 10);
+  }
+
+  @Override
+  public String encode(final String e) {
+    final byte[] b;
+    try {
+      b = e.getBytes("UTF-8");
+    } catch (UnsupportedEncodingException e1) {
+      throw new RuntimeException("No UTF-8 support", e1);
+    }
+
+    final StringBuilder r = new StringBuilder(b.length);
+    for (int i = 0; i < b.length; i++) {
+      final int c = b[i] & 0xff;
+      final char s = safe[c];
+      if (s == 0) {
+        r.append('%');
+        r.append(hexc[c >> 4]);
+        r.append(hexc[c & 15]);
+      } else {
+        r.append(s);
+      }
+    }
+    return r.toString();
+  }
+
+  @Override
+  public String decode(final String e) {
+    if (e.indexOf('%') < 0) {
+      return e.replace('+', ' ');
+    }
+
+    final byte[] b = new byte[e.length()];
+    int bPtr = 0;
+    try {
+      for (int i = 0; i < e.length(); ) {
+        final char c = e.charAt(i);
+        if (c == '%' && i + 2 < e.length()) {
+          final int v = (hexb[e.charAt(i + 1)] << 4) | hexb[e.charAt(i + 2)];
+          if (v < 0) {
+            throw new IllegalArgumentException(e.substring(i, i + 3));
+          }
+          b[bPtr++] = (byte) v;
+          i += 3;
+        } else if (c == '+') {
+          b[bPtr++] = ' ';
+          i++;
+        } else {
+          b[bPtr++] = (byte) c;
+          i++;
+        }
+      }
+    } catch (ArrayIndexOutOfBoundsException err) {
+      throw new IllegalArgumentException("Bad encoding: " + e);
+    }
+    try {
+      return new String(b, 0, bPtr, "UTF-8");
+    } catch (UnsupportedEncodingException e1) {
+      throw new RuntimeException("No UTF-8 support", e1);
+    }
+  }
+}
diff --git a/java/com/google/gwtorm/client/StringKey.java b/java/com/google/gwtorm/client/StringKey.java
new file mode 100644
index 0000000..e56661f
--- /dev/null
+++ b/java/com/google/gwtorm/client/StringKey.java
@@ -0,0 +1,86 @@
+// Copyright 2008 Google Inc.
+//
+// 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.gwtorm.client;
+
+import java.io.Serializable;
+
+/**
+ * Abstract key type using a single string value.
+ *
+ * <p>Applications should subclass this type to create their own entity-specific key classes.
+ *
+ * @param <P> the parent key type. Use {@link Key} if no parent key is needed.
+ */
+@SuppressWarnings("serial")
+public abstract class StringKey<P extends Key<?>>
+    implements Key<P>, Serializable, Comparable<StringKey<?>> {
+  /** @return name of the entity instance. */
+  public abstract String get();
+
+  /** @param newValue the new value of this key. */
+  protected abstract void set(String newValue);
+
+  /** @return the parent key instance; null if this is a root level key. */
+  @Override
+  public P getParentKey() {
+    return null;
+  }
+
+  @Override
+  public int hashCode() {
+    int hc = get() != null ? get().hashCode() : 0;
+    if (getParentKey() != null) {
+      hc *= 31;
+      hc += getParentKey().hashCode();
+    }
+    return hc;
+  }
+
+  @Override
+  public boolean equals(final Object b) {
+    if (b == null || get() == null || b.getClass() != getClass()) {
+      return false;
+    }
+
+    final StringKey<P> q = cast(b);
+    return get().equals(q.get()) && KeyUtil.eq(getParentKey(), q.getParentKey());
+  }
+
+  @Override
+  public int compareTo(final StringKey<?> other) {
+    return get().compareTo(other.get());
+  }
+
+  @Override
+  public String toString() {
+    final StringBuffer r = new StringBuffer();
+    if (getParentKey() != null) {
+      r.append(getParentKey().toString());
+      r.append(',');
+    }
+    r.append(KeyUtil.encode(get()));
+    return r.toString();
+  }
+
+  @Override
+  public void fromString(final String in) {
+    set(KeyUtil.parseFromString(getParentKey(), in));
+  }
+
+  @SuppressWarnings("unchecked")
+  private static <A extends Key<?>> StringKey<A> cast(final Object b) {
+    return (StringKey<A>) b;
+  }
+}
diff --git a/java/gerrit/BUILD b/java/gerrit/BUILD
index 8281d8e..f416f11 100644
--- a/java/gerrit/BUILD
+++ b/java/gerrit/BUILD
@@ -4,10 +4,10 @@
     visibility = ["//visibility:public"],
     deps = [
         "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/reviewdb:server",
         "//java/com/google/gerrit/server",
-        "//lib:gwtorm",
         "//lib/flogger:api",
         "//lib/jgit/org.eclipse.jgit:jgit",
         "//lib/prolog:runtime",
diff --git a/java/gerrit/PRED__load_commit_labels_1.java b/java/gerrit/PRED__load_commit_labels_1.java
index 1d0ba8a..693c89e 100644
--- a/java/gerrit/PRED__load_commit_labels_1.java
+++ b/java/gerrit/PRED__load_commit_labels_1.java
@@ -7,8 +7,6 @@
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.rules.StoredValues;
-import com.google.gwtorm.server.OrmException;
-import com.googlecode.prolog_cafe.exceptions.JavaException;
 import com.googlecode.prolog_cafe.exceptions.PrologException;
 import com.googlecode.prolog_cafe.lang.IntegerTerm;
 import com.googlecode.prolog_cafe.lang.ListTerm;
@@ -36,27 +34,22 @@
     Term a1 = arg1.dereference();
 
     Term listHead = Prolog.Nil;
-    try {
-      ChangeData cd = StoredValues.CHANGE_DATA.get(engine);
-      LabelTypes types = cd.getLabelTypes();
+    ChangeData cd = StoredValues.CHANGE_DATA.get(engine);
+    LabelTypes types = cd.getLabelTypes();
 
-      for (PatchSetApproval a : cd.currentApprovals()) {
-        LabelType t = types.byLabel(a.getLabelId());
-        if (t == null) {
-          continue;
-        }
-
-        StructureTerm labelTerm =
-            new StructureTerm(
-                sym_label, SymbolTerm.intern(t.getName()), new IntegerTerm(a.getValue()));
-
-        StructureTerm userTerm =
-            new StructureTerm(sym_user, new IntegerTerm(a.getAccountId().get()));
-
-        listHead = new ListTerm(new StructureTerm(sym_commit_label, labelTerm, userTerm), listHead);
+    for (PatchSetApproval a : cd.currentApprovals()) {
+      LabelType t = types.byLabel(a.getLabelId());
+      if (t == null) {
+        continue;
       }
-    } catch (OrmException err) {
-      throw new JavaException(this, 1, err);
+
+      StructureTerm labelTerm =
+          new StructureTerm(
+              sym_label, SymbolTerm.intern(t.getName()), new IntegerTerm(a.getValue()));
+
+      StructureTerm userTerm = new StructureTerm(sym_user, new IntegerTerm(a.getAccountId().get()));
+
+      listHead = new ListTerm(new StructureTerm(sym_commit_label, labelTerm, userTerm), listHead);
     }
 
     if (!a1.unify(listHead, engine.trail)) {
diff --git a/java/gerrit/PRED_get_legacy_label_types_1.java b/java/gerrit/PRED_get_legacy_label_types_1.java
index ef79e05..2f0c1ea 100644
--- a/java/gerrit/PRED_get_legacy_label_types_1.java
+++ b/java/gerrit/PRED_get_legacy_label_types_1.java
@@ -17,8 +17,6 @@
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.LabelValue;
 import com.google.gerrit.server.rules.StoredValues;
-import com.google.gwtorm.server.OrmException;
-import com.googlecode.prolog_cafe.exceptions.JavaException;
 import com.googlecode.prolog_cafe.exceptions.PrologException;
 import com.googlecode.prolog_cafe.lang.IntegerTerm;
 import com.googlecode.prolog_cafe.lang.ListTerm;
@@ -53,12 +51,7 @@
   public Operation exec(Prolog engine) throws PrologException {
     engine.setB0();
     Term a1 = arg1.dereference();
-    List<LabelType> list;
-    try {
-      list = StoredValues.CHANGE_DATA.get(engine).getLabelTypes().getLabelTypes();
-    } catch (OrmException err) {
-      throw new JavaException(this, 1, err);
-    }
+    List<LabelType> list = StoredValues.CHANGE_DATA.get(engine).getLabelTypes().getLabelTypes();
     Term head = Prolog.Nil;
     for (int idx = list.size() - 1; 0 <= idx; idx--) {
       head = new ListTerm(export(list.get(idx)), head);
diff --git a/java/gerrit/PRED_pure_revert_1.java b/java/gerrit/PRED_pure_revert_1.java
index 95a0729..6300a668 100644
--- a/java/gerrit/PRED_pure_revert_1.java
+++ b/java/gerrit/PRED_pure_revert_1.java
@@ -15,8 +15,6 @@
 package gerrit;
 
 import com.google.gerrit.server.rules.StoredValues;
-import com.google.gwtorm.server.OrmException;
-import com.googlecode.prolog_cafe.exceptions.JavaException;
 import com.googlecode.prolog_cafe.exceptions.PrologException;
 import com.googlecode.prolog_cafe.lang.IntegerTerm;
 import com.googlecode.prolog_cafe.lang.Operation;
@@ -36,12 +34,7 @@
     engine.setB0();
     Term a1 = arg1.dereference();
 
-    Boolean isPureRevert;
-    try {
-      isPureRevert = StoredValues.CHANGE_DATA.get(engine).isPureRevert();
-    } catch (OrmException e) {
-      throw new JavaException(this, 1, e);
-    }
+    Boolean isPureRevert = StoredValues.CHANGE_DATA.get(engine).isPureRevert();
     if (!a1.unify(new IntegerTerm(Boolean.TRUE.equals(isPureRevert) ? 1 : 0), engine.trail)) {
       return engine.fail();
     }
diff --git a/java/gerrit/PRED_unresolved_comments_count_1.java b/java/gerrit/PRED_unresolved_comments_count_1.java
index 5ed1525..d4abcc54 100644
--- a/java/gerrit/PRED_unresolved_comments_count_1.java
+++ b/java/gerrit/PRED_unresolved_comments_count_1.java
@@ -15,8 +15,6 @@
 package gerrit;
 
 import com.google.gerrit.server.rules.StoredValues;
-import com.google.gwtorm.server.OrmException;
-import com.googlecode.prolog_cafe.exceptions.JavaException;
 import com.googlecode.prolog_cafe.exceptions.PrologException;
 import com.googlecode.prolog_cafe.lang.IntegerTerm;
 import com.googlecode.prolog_cafe.lang.Operation;
@@ -35,13 +33,9 @@
     engine.setB0();
     Term a1 = arg1.dereference();
 
-    try {
-      Integer count = StoredValues.CHANGE_DATA.get(engine).unresolvedCommentCount();
-      if (!a1.unify(new IntegerTerm(count != null ? count : 0), engine.trail)) {
-        return engine.fail();
-      }
-    } catch (OrmException err) {
-      throw new JavaException(this, 1, err);
+    Integer count = StoredValues.CHANGE_DATA.get(engine).unresolvedCommentCount();
+    if (!a1.unify(new IntegerTerm(count != null ? count : 0), engine.trail)) {
+      return engine.fail();
     }
     return cont;
   }
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 37b4559..0c21068 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -47,6 +47,7 @@
 import com.google.common.collect.Iterables;
 import com.google.common.io.BaseEncoding;
 import com.google.common.truth.Correspondence;
+import com.google.common.truth.Correspondence.BinaryPredicate;
 import com.google.common.util.concurrent.AtomicLongMap;
 import com.google.common.util.concurrent.Runnables;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -66,6 +67,7 @@
 import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.common.data.PermissionRule.Action;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.api.accounts.AccountInput;
 import com.google.gerrit.extensions.api.accounts.DeleteDraftCommentsInput;
 import com.google.gerrit.extensions.api.accounts.DeletedDraftCommentInfo;
@@ -134,7 +136,6 @@
 import com.google.gerrit.testing.ConfigSuite;
 import com.google.gerrit.testing.FakeEmailSender.Message;
 import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.name.Named;
@@ -313,19 +314,19 @@
   private Account.Id createByAccountCreator(int expectedAccountReindexCalls) throws Exception {
     String name = "foo";
     TestAccount foo = accountCreator.create(name);
-    AccountInfo info = gApi.accounts().id(foo.id.get()).get();
+    AccountInfo info = gApi.accounts().id(foo.id().get()).get();
     assertThat(info.username).isEqualTo(name);
     assertThat(info.name).isEqualTo(name);
     accountIndexedCounter.assertReindexOf(foo, expectedAccountReindexCalls);
-    assertUserBranch(foo.getId(), name, null);
-    return foo.getId();
+    assertUserBranch(foo.id(), name, null);
+    return foo.id();
   }
 
   @Test
   public void createAnonymousCowardByAccountCreator() throws Exception {
     TestAccount anonymousCoward = accountCreator.create();
     accountIndexedCounter.assertReindexOf(anonymousCoward);
-    assertUserBranchWithoutAccountConfig(anonymousCoward.getId());
+    assertUserBranchWithoutAccountConfig(anonymousCoward.id());
   }
 
   @Test
@@ -352,10 +353,10 @@
   @Test
   public void createAccountUsernameAlreadyTaken() throws Exception {
     AccountInput input = new AccountInput();
-    input.username = admin.username;
+    input.username = admin.username();
 
     exception.expect(ResourceConflictException.class);
-    exception.expectMessage("username '" + admin.username + "' already exists");
+    exception.expectMessage("username '" + admin.username() + "' already exists");
     gApi.accounts().create(input);
   }
 
@@ -363,10 +364,10 @@
   public void createAccountEmailAlreadyTaken() throws Exception {
     AccountInput input = new AccountInput();
     input.username = "foo";
-    input.email = admin.email;
+    input.email = admin.email();
 
     exception.expect(UnprocessableEntityException.class);
-    exception.expectMessage("email '" + admin.email + "' already exists");
+    exception.expectMessage("email '" + admin.email() + "' already exists");
     gApi.accounts().create(input);
   }
 
@@ -438,18 +439,18 @@
   @Test
   public void updateAccountWithoutAccountConfigNoteDb() throws Exception {
     TestAccount anonymousCoward = accountCreator.create();
-    assertUserBranchWithoutAccountConfig(anonymousCoward.getId());
+    assertUserBranchWithoutAccountConfig(anonymousCoward.id());
 
     String status = "OOO";
     Optional<AccountState> accountState =
         accountsUpdateProvider
             .get()
-            .update("Set status", anonymousCoward.getId(), u -> u.setStatus(status));
+            .update("Set status", anonymousCoward.id(), u -> u.setStatus(status));
     assertThat(accountState).isPresent();
     Account account = accountState.get().getAccount();
     assertThat(account.getFullName()).isNull();
     assertThat(account.getStatus()).isEqualTo(status);
-    assertUserBranch(anonymousCoward.getId(), null, status);
+    assertUserBranch(anonymousCoward.id(), null, status);
   }
 
   private void assertUserBranchWithoutAccountConfig(Account.Id accountId) throws Exception {
@@ -660,7 +661,7 @@
     assertThat(change.stars).contains(DEFAULT_LABEL);
     refUpdateCounter.assertRefUpdateFor(
         RefUpdateCounter.projectRef(
-            allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id)));
+            allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id())));
 
     gApi.accounts().self().unstarChange(triplet);
     change = info(triplet);
@@ -668,7 +669,7 @@
     assertThat(change.stars).isNull();
     refUpdateCounter.assertRefUpdateFor(
         RefUpdateCounter.projectRef(
-            allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id)));
+            allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id())));
 
     accountIndexedCounter.assertNoReindex();
   }
@@ -699,7 +700,7 @@
     assertThat(starredChange.stars).containsExactly("blue", "red", DEFAULT_LABEL).inOrder();
     refUpdateCounter.assertRefUpdateFor(
         RefUpdateCounter.projectRef(
-            allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id)));
+            allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id())));
 
     gApi.accounts()
         .self()
@@ -718,14 +719,14 @@
     assertThat(starredChange.stars).containsExactly("red", "yellow").inOrder();
     refUpdateCounter.assertRefUpdateFor(
         RefUpdateCounter.projectRef(
-            allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id)));
+            allUsers, RefNames.refsStarredChanges(new Change.Id(change._number), admin.id())));
 
     accountIndexedCounter.assertNoReindex();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("not allowed to get stars of another account");
-    gApi.accounts().id(Integer.toString((admin.id.get()))).getStars(triplet);
+    gApi.accounts().id(Integer.toString((admin.id().get()))).getStars(triplet);
   }
 
   @Test
@@ -778,22 +779,22 @@
     PushOneCommit.Result r = createChange();
 
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     gApi.changes().id(r.getChangeId()).addReviewer(in);
 
     in = new AddReviewerInput();
-    in.reviewer = user2.email;
+    in.reviewer = user2.email();
     gApi.changes().id(r.getChangeId()).addReviewer(in);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.accounts().self().setStars(r.getChangeId(), new StarsInput(ImmutableSet.of(IGNORE_LABEL)));
 
     sender.clear();
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(r.getChangeId()).abandon();
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
-    assertThat(messages.get(0).rcpt()).containsExactly(user2.emailAddress);
+    assertThat(messages.get(0).rcpt()).containsExactly(user2.getEmailAddress());
     accountIndexedCounter.assertNoReindex();
   }
 
@@ -801,20 +802,20 @@
   public void addReviewerToIgnoredChange() throws Exception {
     PushOneCommit.Result r = createChange();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.accounts().self().setStars(r.getChangeId(), new StarsInput(ImmutableSet.of(IGNORE_LABEL)));
 
     sender.clear();
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
 
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     gApi.changes().id(r.getChangeId()).addReviewer(in);
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message message = messages.get(0);
-    assertThat(message.rcpt()).containsExactly(user.emailAddress);
-    assertMailReplyTo(message, admin.email);
+    assertThat(message.rcpt()).containsExactly(user.getEmailAddress());
+    assertMailReplyTo(message, admin.email());
     accountIndexedCounter.assertNoReindex();
   }
 
@@ -841,20 +842,20 @@
     TestAccount foo = accountCreator.create(username, email, name);
     String secondaryEmail = "secondary@example.com";
     EmailInput input = newEmailInput(secondaryEmail);
-    gApi.accounts().id(foo.id.get()).addEmail(input);
+    gApi.accounts().id(foo.id().get()).addEmail(input);
 
     String status = "OOO";
-    gApi.accounts().id(foo.id.get()).setStatus(status);
+    gApi.accounts().id(foo.id().get()).setStatus(status);
 
-    requestScopeOperations.setApiUser(foo.getId());
-    AccountDetailInfo detail = gApi.accounts().id(foo.id.get()).detail();
-    assertThat(detail._accountId).isEqualTo(foo.id.get());
+    requestScopeOperations.setApiUser(foo.id());
+    AccountDetailInfo detail = gApi.accounts().id(foo.id().get()).detail();
+    assertThat(detail._accountId).isEqualTo(foo.id().get());
     assertThat(detail.name).isEqualTo(name);
     assertThat(detail.username).isEqualTo(username);
     assertThat(detail.email).isEqualTo(email);
     assertThat(detail.secondaryEmails).containsExactly(secondaryEmail);
     assertThat(detail.status).isEqualTo(status);
-    assertThat(detail.registeredOn).isEqualTo(getAccount(foo.getId()).getRegisteredOn());
+    assertThat(detail.registeredOn).isEqualTo(getAccount(foo.id()).getRegisteredOn());
     assertThat(detail.inactive).isNull();
     assertThat(detail._moreAccounts).isNull();
   }
@@ -866,10 +867,10 @@
     TestAccount foo = accountCreator.create(name("foo"), email, "Foo");
     String secondaryEmail = "secondary@example.com";
     EmailInput input = newEmailInput(secondaryEmail);
-    gApi.accounts().id(foo.id.get()).addEmail(input);
+    gApi.accounts().id(foo.id().get()).addEmail(input);
 
-    requestScopeOperations.setApiUser(user.getId());
-    AccountDetailInfo detail = gApi.accounts().id(foo.id.get()).detail();
+    requestScopeOperations.setApiUser(user.id());
+    AccountDetailInfo detail = gApi.accounts().id(foo.id().get()).detail();
     assertThat(detail.secondaryEmails).isNull();
   }
 
@@ -879,9 +880,9 @@
     TestAccount foo = accountCreator.create(name("foo"), email, "Foo");
     String secondaryEmail = "secondary@example.com";
     EmailInput input = newEmailInput(secondaryEmail);
-    gApi.accounts().id(foo.id.get()).addEmail(input);
+    gApi.accounts().id(foo.id().get()).addEmail(input);
 
-    AccountDetailInfo detail = gApi.accounts().id(foo.id.get()).detail();
+    AccountDetailInfo detail = gApi.accounts().id(foo.id().get()).detail();
     assertThat(detail.secondaryEmails).containsExactly(secondaryEmail);
   }
 
@@ -890,15 +891,15 @@
     String email = "preferred@example.com";
     TestAccount foo = accountCreator.create(name("foo"), email, "Foo");
 
-    requestScopeOperations.setApiUser(foo.getId());
+    requestScopeOperations.setApiUser(foo.id());
     assertThat(getEmails()).containsExactly(email);
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     String secondaryEmail = "secondary@example.com";
     EmailInput input = newEmailInput(secondaryEmail);
-    gApi.accounts().id(foo.id.hashCode()).addEmail(input);
+    gApi.accounts().id(foo.id().hashCode()).addEmail(input);
 
-    requestScopeOperations.setApiUser(foo.getId());
+    requestScopeOperations.setApiUser(foo.id());
     assertThat(getEmails()).containsExactly(email, secondaryEmail);
   }
 
@@ -907,10 +908,10 @@
     String email = "preferred2@example.com";
     TestAccount foo = accountCreator.create(name("foo"), email, "Foo");
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("modify account not permitted");
-    gApi.accounts().id(foo.id.get()).getEmails();
+    gApi.accounts().id(foo.id().get()).getEmails();
   }
 
   @Test
@@ -919,10 +920,10 @@
     String secondaryEmail = "secondary3@example.com";
     TestAccount foo = accountCreator.create(name("foo"), email, "Foo");
     EmailInput input = newEmailInput(secondaryEmail);
-    gApi.accounts().id(foo.id.hashCode()).addEmail(input);
+    gApi.accounts().id(foo.id().hashCode()).addEmail(input);
 
     assertThat(
-            gApi.accounts().id(foo.id.get()).getEmails().stream()
+            gApi.accounts().id(foo.id().get()).getEmails().stream()
                 .map(e -> e.email)
                 .collect(toSet()))
         .containsExactly(email, secondaryEmail);
@@ -940,7 +941,7 @@
     }
 
     requestScopeOperations.resetCurrentApiUser();
-    assertThat(getEmails()).containsAllIn(emails);
+    assertThat(getEmails()).containsAtLeastElementsIn(emails);
   }
 
   @Test
@@ -974,9 +975,9 @@
   public void cannotAddNonConfirmedEmailWithoutModifyAccountPermission() throws Exception {
     TestAccount account = accountCreator.create(name("user"));
     EmailInput input = newEmailInput("test@test.com");
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
-    gApi.accounts().id(account.username).addEmail(input);
+    gApi.accounts().id(account.username()).addEmail(input);
   }
 
   @Test
@@ -986,7 +987,7 @@
     gApi.accounts().self().addEmail(input);
     exception.expect(ResourceConflictException.class);
     exception.expectMessage("Identity 'mailto:" + email + "' in use by another account");
-    gApi.accounts().id(user.username).addEmail(input);
+    gApi.accounts().id(user.username()).addEmail(input);
   }
 
   @Test
@@ -1009,7 +1010,7 @@
       value = "HsOc6l+2lhS9G7sE/RsnS7Z6GJjdRDX14co=")
   public void addEmailToBeConfirmedToOwnAccount() throws Exception {
     TestAccount user = accountCreator.create();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     String email = "self@example.com";
     EmailInput input = newEmailInput(email, false);
@@ -1020,11 +1021,11 @@
   public void cannotAddEmailToBeConfirmedToOtherAccountWithoutModifyAccountPermission()
       throws Exception {
     TestAccount user = accountCreator.create();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     exception.expect(AuthException.class);
     exception.expectMessage("modify account not permitted");
-    gApi.accounts().id(admin.id.get()).addEmail(newEmailInput("foo@example.com", false));
+    gApi.accounts().id(admin.id().get()).addEmail(newEmailInput("foo@example.com", false));
   }
 
   @Test
@@ -1034,7 +1035,7 @@
   public void addEmailToBeConfirmedToOtherAccount() throws Exception {
     TestAccount user = accountCreator.create();
     String email = "me@example.com";
-    gApi.accounts().id(user.id.get()).addEmail(newEmailInput(email, false));
+    gApi.accounts().id(user.id().get()).addEmail(newEmailInput(email, false));
   }
 
   @Test
@@ -1063,16 +1064,17 @@
         .get()
         .update(
             "Add External IDs",
-            admin.id,
+            admin.id(),
             u ->
                 u.addExternalId(
-                        ExternalId.createWithEmail(ExternalId.Key.parse(extId1), admin.id, email))
+                        ExternalId.createWithEmail(ExternalId.Key.parse(extId1), admin.id(), email))
                     .addExternalId(
-                        ExternalId.createWithEmail(ExternalId.Key.parse(extId2), admin.id, email)));
+                        ExternalId.createWithEmail(
+                            ExternalId.Key.parse(extId2), admin.id(), email)));
     accountIndexedCounter.assertReindexOf(admin);
     assertThat(
             gApi.accounts().self().getExternalIds().stream().map(e -> e.identity).collect(toSet()))
-        .containsAllOf(extId1, extId2);
+        .containsAtLeast(extId1, extId2);
 
     requestScopeOperations.resetCurrentApiUser();
     assertThat(getEmails()).contains(email);
@@ -1093,30 +1095,30 @@
     EmailInput input = new EmailInput();
     input.email = email;
     input.noConfirmation = true;
-    gApi.accounts().id(user.id.get()).addEmail(input);
+    gApi.accounts().id(user.id().get()).addEmail(input);
     accountIndexedCounter.assertReindexOf(user);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertThat(getEmails()).contains(email);
 
     // admin can delete email of user
-    requestScopeOperations.setApiUser(admin.getId());
-    gApi.accounts().id(user.id.get()).deleteEmail(email);
+    requestScopeOperations.setApiUser(admin.id());
+    gApi.accounts().id(user.id().get()).deleteEmail(email);
     accountIndexedCounter.assertReindexOf(user);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertThat(getEmails()).doesNotContain(email);
 
     // user cannot delete email of admin
     exception.expect(AuthException.class);
     exception.expectMessage("modify account not permitted");
-    gApi.accounts().id(admin.id.get()).deleteEmail(admin.email);
+    gApi.accounts().id(admin.id().get()).deleteEmail(admin.email());
   }
 
   @Test
   public void lookUpByEmail() throws Exception {
     // exact match with scheme "mailto:"
-    assertEmail(emails.getAccountFor(admin.email), admin);
+    assertEmail(emails.getAccountFor(admin.email()), admin);
 
     // exact match with other scheme
     String email = "foo.bar@example.com";
@@ -1124,26 +1126,28 @@
         .get()
         .update(
             "Add Email",
-            admin.id,
+            admin.id(),
             u ->
                 u.addExternalId(
-                    ExternalId.createWithEmail(ExternalId.Key.parse("foo:bar"), admin.id, email)));
+                    ExternalId.createWithEmail(
+                        ExternalId.Key.parse("foo:bar"), admin.id(), email)));
     assertEmail(emails.getAccountFor(email), admin);
 
     // wrong case doesn't match
-    assertThat(emails.getAccountFor(admin.email.toUpperCase(Locale.US))).isEmpty();
+    assertThat(emails.getAccountFor(admin.email().toUpperCase(Locale.US))).isEmpty();
 
     // prefix doesn't match
-    assertThat(emails.getAccountFor(admin.email.substring(0, admin.email.indexOf('@')))).isEmpty();
+    assertThat(emails.getAccountFor(admin.email().substring(0, admin.email().indexOf('@'))))
+        .isEmpty();
 
     // non-existing doesn't match
     assertThat(emails.getAccountFor("non-existing@example.com")).isEmpty();
 
     // lookup several accounts by email at once
     ImmutableSetMultimap<String, Account.Id> byEmails =
-        emails.getAccountsFor(admin.email, user.email);
-    assertEmail(byEmails.get(admin.email), admin);
-    assertEmail(byEmails.get(user.email), user);
+        emails.getAccountsFor(admin.email(), user.email());
+    assertEmail(byEmails.get(admin.email()), admin);
+    assertEmail(byEmails.get(user.email()), user);
   }
 
   @Test
@@ -1154,12 +1158,12 @@
     TestAccount foo = accountCreator.create(name("foo"));
     accountsUpdateProvider
         .get()
-        .update("Set Preferred Email", foo.id, u -> u.setPreferredEmail(prefEmail));
+        .update("Set Preferred Email", foo.id(), u -> u.setPreferredEmail(prefEmail));
 
     // verify that the account is still found when using the preferred email to lookup the account
     ImmutableSet<Account.Id> accountsByPrefEmail = emails.getAccountFor(prefEmail);
     assertThat(accountsByPrefEmail).hasSize(1);
-    assertThat(Iterables.getOnlyElement(accountsByPrefEmail)).isEqualTo(foo.id);
+    assertThat(Iterables.getOnlyElement(accountsByPrefEmail)).isEqualTo(foo.id());
 
     // look up by email prefix doesn't find the account
     accountsByPrefEmail = emails.getAccountFor(prefix);
@@ -1195,31 +1199,31 @@
 
   @Test
   public void adminCanSetNameOfOtherUser() throws Exception {
-    gApi.accounts().id(user.username).setName("User McUserface");
-    assertThat(gApi.accounts().id(user.username).get().name).isEqualTo("User McUserface");
+    gApi.accounts().id(user.username()).setName("User McUserface");
+    assertThat(gApi.accounts().id(user.username()).get().name).isEqualTo("User McUserface");
   }
 
   @Test
   public void userCannotSetNameOfOtherUser() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
-    gApi.accounts().id(admin.username).setName("Admin McAdminface");
+    gApi.accounts().id(admin.username()).setName("Admin McAdminface");
   }
 
   @Test
   @Sandboxed
   public void userCanSetNameOfOtherUserWithModifyAccountPermission() throws Exception {
     allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.MODIFY_ACCOUNT);
-    gApi.accounts().id(admin.username).setName("Admin McAdminface");
-    assertThat(gApi.accounts().id(admin.username).get().name).isEqualTo("Admin McAdminface");
+    gApi.accounts().id(admin.username()).setName("Admin McAdminface");
+    assertThat(gApi.accounts().id(admin.username()).get().name).isEqualTo("Admin McAdminface");
   }
 
   @Test
   public void fetchUserBranch() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, user);
-    String userRefName = RefNames.refsUsers(user.id);
+    String userRefName = RefNames.refsUsers(user.id());
 
     // remove default READ permissions
     try (ProjectConfigUpdate u = updateProject(allUsers)) {
@@ -1262,7 +1266,7 @@
     accountIndexedCounter.assertNoReindex();
 
     // fetching user branch of another user fails
-    String otherUserRefName = RefNames.refsUsers(admin.id);
+    String otherUserRefName = RefNames.refsUsers(admin.id());
     exception.expect(TransportException.class);
     exception.expectMessage("Remote does not have " + otherUserRefName + " available for fetch.");
     fetch(allUsersRepo, otherUserRefName + ":otherUserRef");
@@ -1271,24 +1275,24 @@
   @Test
   public void pushToUserBranch() throws Exception {
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
+    fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
     allUsersRepo.reset("userRef");
-    PushOneCommit push = pushFactory.create(admin.getIdent(), allUsersRepo);
-    push.to(RefNames.refsUsers(admin.id)).assertOkStatus();
+    PushOneCommit push = pushFactory.create(admin.newIdent(), allUsersRepo);
+    push.to(RefNames.refsUsers(admin.id())).assertOkStatus();
     accountIndexedCounter.assertReindexOf(admin);
 
-    push = pushFactory.create(admin.getIdent(), allUsersRepo);
+    push = pushFactory.create(admin.newIdent(), allUsersRepo);
     push.to(RefNames.REFS_USERS_SELF).assertOkStatus();
     accountIndexedCounter.assertReindexOf(admin);
   }
 
   @Test
   public void pushToUserBranchForReview() throws Exception {
-    String userRefName = RefNames.refsUsers(admin.id);
+    String userRefName = RefNames.refsUsers(admin.id());
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
     fetch(allUsersRepo, userRefName + ":userRef");
     allUsersRepo.reset("userRef");
-    PushOneCommit push = pushFactory.create(admin.getIdent(), allUsersRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), allUsersRepo);
     PushOneCommit.Result r = push.to(MagicBranch.NEW_CHANGE + userRefName);
     r.assertOkStatus();
     accountIndexedCounter.assertNoReindex();
@@ -1297,7 +1301,7 @@
     gApi.changes().id(r.getChangeId()).current().submit();
     accountIndexedCounter.assertReindexOf(admin);
 
-    push = pushFactory.create(admin.getIdent(), allUsersRepo);
+    push = pushFactory.create(admin.newIdent(), allUsersRepo);
     r = push.to(MagicBranch.NEW_CHANGE + RefNames.REFS_USERS_SELF);
     r.assertOkStatus();
     accountIndexedCounter.assertNoReindex();
@@ -1309,7 +1313,7 @@
 
   @Test
   public void pushAccountConfigToUserBranchForReviewAndSubmit() throws Exception {
-    String userRef = RefNames.refsUsers(admin.id);
+    String userRef = RefNames.refsUsers(admin.id());
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
     fetch(allUsersRepo, userRef + ":userRef");
     allUsersRepo.reset("userRef");
@@ -1320,7 +1324,7 @@
     PushOneCommit.Result r =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 allUsersRepo,
                 "Update account config",
                 AccountProperties.ACCOUNT_CONFIG,
@@ -1335,8 +1339,8 @@
     accountIndexedCounter.assertReindexOf(admin);
 
     AccountInfo info = gApi.accounts().self().get();
-    assertThat(info.email).isEqualTo(admin.email);
-    assertThat(info.name).isEqualTo(admin.fullName);
+    assertThat(info.email).isEqualTo(admin.email());
+    assertThat(info.name).isEqualTo(admin.fullName());
     assertThat(info.status).isEqualTo("out-of-office");
   }
 
@@ -1344,7 +1348,7 @@
   public void pushAccountConfigWithPrefEmailThatDoesNotExistAsExtIdToUserBranchForReviewAndSubmit()
       throws Exception {
     TestAccount foo = accountCreator.create(name("foo"), name("foo") + "@example.com", "Foo");
-    String userRef = RefNames.refsUsers(foo.id);
+    String userRef = RefNames.refsUsers(foo.id());
     accountIndexedCounter.clear();
 
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, foo);
@@ -1358,7 +1362,7 @@
     PushOneCommit.Result r =
         pushFactory
             .create(
-                foo.getIdent(),
+                foo.newIdent(),
                 allUsersRepo,
                 "Update account config",
                 AccountProperties.ACCOUNT_CONFIG,
@@ -1368,7 +1372,7 @@
     accountIndexedCounter.assertNoReindex();
     assertThat(r.getChange().change().getDest().get()).isEqualTo(userRef);
 
-    requestScopeOperations.setApiUser(foo.getId());
+    requestScopeOperations.setApiUser(foo.id());
     gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
     gApi.changes().id(r.getChangeId()).current().submit();
 
@@ -1376,13 +1380,13 @@
 
     AccountInfo info = gApi.accounts().self().get();
     assertThat(info.email).isEqualTo(email);
-    assertThat(info.name).isEqualTo(foo.fullName);
+    assertThat(info.name).isEqualTo(foo.fullName());
   }
 
   @Test
   public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfConfigIsInvalid()
       throws Exception {
-    String userRef = RefNames.refsUsers(admin.id);
+    String userRef = RefNames.refsUsers(admin.id());
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
     fetch(allUsersRepo, userRef + ":userRef");
     allUsersRepo.reset("userRef");
@@ -1390,7 +1394,7 @@
     PushOneCommit.Result r =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 allUsersRepo,
                 "Update account config",
                 AccountProperties.ACCOUNT_CONFIG,
@@ -1408,7 +1412,7 @@
                 + " Invalid config file %s in commit %s",
             r.getCommit().name(),
             AccountProperties.ACCOUNT_CONFIG,
-            admin.id,
+            admin.id(),
             AccountProperties.ACCOUNT_CONFIG,
             r.getCommit().name()));
     gApi.changes().id(r.getChangeId()).current().submit();
@@ -1417,7 +1421,7 @@
   @Test
   public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfPreferredEmailIsInvalid()
       throws Exception {
-    String userRef = RefNames.refsUsers(admin.id);
+    String userRef = RefNames.refsUsers(admin.id());
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
     fetch(allUsersRepo, userRef + ":userRef");
     allUsersRepo.reset("userRef");
@@ -1429,7 +1433,7 @@
     PushOneCommit.Result r =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 allUsersRepo,
                 "Update account config",
                 AccountProperties.ACCOUNT_CONFIG,
@@ -1444,14 +1448,14 @@
     exception.expectMessage(
         String.format(
             "invalid account configuration: invalid preferred email '%s' for account '%s'",
-            noEmail, admin.id));
+            noEmail, admin.id()));
     gApi.changes().id(r.getChangeId()).current().submit();
   }
 
   @Test
   public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmitIfOwnAccountIsDeactivated()
       throws Exception {
-    String userRef = RefNames.refsUsers(admin.id);
+    String userRef = RefNames.refsUsers(admin.id());
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
     fetch(allUsersRepo, userRef + ":userRef");
     allUsersRepo.reset("userRef");
@@ -1462,7 +1466,7 @@
     PushOneCommit.Result r =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 allUsersRepo,
                 "Update account config",
                 AccountProperties.ACCOUNT_CONFIG,
@@ -1483,8 +1487,8 @@
     allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
 
     TestAccount foo = accountCreator.create(name("foo"));
-    assertThat(gApi.accounts().id(foo.id.get()).getActive()).isTrue();
-    String userRef = RefNames.refsUsers(foo.id);
+    assertThat(gApi.accounts().id(foo.id().get()).getActive()).isTrue();
+    String userRef = RefNames.refsUsers(foo.id());
     accountIndexedCounter.clear();
 
     grant(allUsers, userRef, Permission.PUSH, false, adminGroupUuid());
@@ -1501,7 +1505,7 @@
     PushOneCommit.Result r =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 allUsersRepo,
                 "Update account config",
                 AccountProperties.ACCOUNT_CONFIG,
@@ -1515,13 +1519,13 @@
     gApi.changes().id(r.getChangeId()).current().submit();
     accountIndexedCounter.assertReindexOf(foo);
 
-    assertThat(gApi.accounts().id(foo.id.get()).getActive()).isFalse();
+    assertThat(gApi.accounts().id(foo.id().get()).getActive()).isFalse();
   }
 
   @Test
   public void pushWatchConfigToUserBranch() throws Exception {
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
+    fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
     allUsersRepo.reset("userRef");
 
     Config wc = new Config();
@@ -1532,7 +1536,7 @@
         ProjectWatches.NotifyValue.create(null, EnumSet.of(NotifyType.ALL_COMMENTS)).toString());
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             allUsersRepo,
             "Add project watch",
             ProjectWatches.WATCH_CONFIG,
@@ -1545,7 +1549,7 @@
         ProjectWatches.PROJECT, project.get(), ProjectWatches.KEY_NOTIFY, invalidNotifyValue);
     push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             allUsersRepo,
             "Add invalid project watch",
             ProjectWatches.WATCH_CONFIG,
@@ -1555,17 +1559,17 @@
     r.assertMessage(
         String.format(
             "%s: Invalid project watch of account %d for project %s: %s",
-            ProjectWatches.WATCH_CONFIG, admin.getId().get(), project.get(), invalidNotifyValue));
+            ProjectWatches.WATCH_CONFIG, admin.id().get(), project.get(), invalidNotifyValue));
   }
 
   @Test
   public void pushAccountConfigToUserBranch() throws Exception {
     TestAccount oooUser = accountCreator.create("away", "away@mail.invalid", "Ambrose Way");
-    requestScopeOperations.setApiUser(oooUser.getId());
+    requestScopeOperations.setApiUser(oooUser.id());
 
     // Must clone as oooUser to ensure the push is allowed.
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers, oooUser);
-    fetch(allUsersRepo, RefNames.refsUsers(oooUser.id) + ":userRef");
+    fetch(allUsersRepo, RefNames.refsUsers(oooUser.id()) + ":userRef");
     allUsersRepo.reset("userRef");
 
     Config ac = getAccountConfig(allUsersRepo);
@@ -1574,32 +1578,32 @@
     accountIndexedCounter.clear();
     pushFactory
         .create(
-            oooUser.getIdent(),
+            oooUser.newIdent(),
             allUsersRepo,
             "Update account config",
             AccountProperties.ACCOUNT_CONFIG,
             ac.toText())
-        .to(RefNames.refsUsers(oooUser.id))
+        .to(RefNames.refsUsers(oooUser.id()))
         .assertOkStatus();
 
     accountIndexedCounter.assertReindexOf(oooUser);
 
     AccountInfo info = gApi.accounts().self().get();
-    assertThat(info.email).isEqualTo(oooUser.email);
-    assertThat(info.name).isEqualTo(oooUser.fullName);
+    assertThat(info.email).isEqualTo(oooUser.email());
+    assertThat(info.name).isEqualTo(oooUser.fullName());
     assertThat(info.status).isEqualTo("out-of-office");
   }
 
   @Test
   public void pushAccountConfigToUserBranchIsRejectedIfConfigIsInvalid() throws Exception {
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
+    fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
     allUsersRepo.reset("userRef");
 
     PushOneCommit.Result r =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 allUsersRepo,
                 "Update account config",
                 AccountProperties.ACCOUNT_CONFIG,
@@ -1612,7 +1616,7 @@
                 + " Invalid config file %s in commit %s",
             r.getCommit().name(),
             AccountProperties.ACCOUNT_CONFIG,
-            admin.id,
+            admin.id(),
             AccountProperties.ACCOUNT_CONFIG,
             r.getCommit().name()));
     accountIndexedCounter.assertNoReindex();
@@ -1621,7 +1625,7 @@
   @Test
   public void pushAccountConfigToUserBranchIsRejectedIfPreferredEmailIsInvalid() throws Exception {
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
+    fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
     allUsersRepo.reset("userRef");
 
     String noEmail = "no.email";
@@ -1631,7 +1635,7 @@
     PushOneCommit.Result r =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 allUsersRepo,
                 "Update account config",
                 AccountProperties.ACCOUNT_CONFIG,
@@ -1639,19 +1643,19 @@
             .to(RefNames.REFS_USERS_SELF);
     r.assertErrorStatus("invalid account configuration");
     r.assertMessage(
-        String.format("invalid preferred email '%s' for account '%s'", noEmail, admin.id));
+        String.format("invalid preferred email '%s' for account '%s'", noEmail, admin.id()));
     accountIndexedCounter.assertNoReindex();
   }
 
   @Test
   public void pushAccountConfigToUserBranchInvalidPreferredEmailButNotChanged() throws Exception {
     TestAccount foo = accountCreator.create(name("foo"), name("foo") + "@example.com", "Foo");
-    String userRef = RefNames.refsUsers(foo.id);
+    String userRef = RefNames.refsUsers(foo.id());
 
     String noEmail = "no.email";
     accountsUpdateProvider
         .get()
-        .update("Set Preferred Email", foo.id, u -> u.setPreferredEmail(noEmail));
+        .update("Set Preferred Email", foo.id(), u -> u.setPreferredEmail(noEmail));
     accountIndexedCounter.clear();
 
     grant(allUsers, userRef, Permission.PUSH, false, REGISTERED_USERS);
@@ -1665,7 +1669,7 @@
 
     pushFactory
         .create(
-            foo.getIdent(),
+            foo.newIdent(),
             allUsersRepo,
             "Update account config",
             AccountProperties.ACCOUNT_CONFIG,
@@ -1674,16 +1678,16 @@
         .assertOkStatus();
     accountIndexedCounter.assertReindexOf(foo);
 
-    AccountInfo info = gApi.accounts().id(foo.id.get()).get();
+    AccountInfo info = gApi.accounts().id(foo.id().get()).get();
     assertThat(info.email).isEqualTo(noEmail);
-    assertThat(info.name).isEqualTo(foo.fullName);
+    assertThat(info.name).isEqualTo(foo.fullName());
     assertThat(info.status).isEqualTo(status);
   }
 
   @Test
   public void pushAccountConfigToUserBranchIfPreferredEmailDoesNotExistAsExtId() throws Exception {
     TestAccount foo = accountCreator.create(name("foo"), name("foo") + "@example.com", "Foo");
-    String userRef = RefNames.refsUsers(foo.id);
+    String userRef = RefNames.refsUsers(foo.id());
     accountIndexedCounter.clear();
 
     grant(allUsers, userRef, Permission.PUSH, false, adminGroupUuid());
@@ -1698,7 +1702,7 @@
 
     pushFactory
         .create(
-            foo.getIdent(),
+            foo.newIdent(),
             allUsersRepo,
             "Update account config",
             AccountProperties.ACCOUNT_CONFIG,
@@ -1707,15 +1711,15 @@
         .assertOkStatus();
     accountIndexedCounter.assertReindexOf(foo);
 
-    AccountInfo info = gApi.accounts().id(foo.id.get()).get();
+    AccountInfo info = gApi.accounts().id(foo.id().get()).get();
     assertThat(info.email).isEqualTo(email);
-    assertThat(info.name).isEqualTo(foo.fullName);
+    assertThat(info.name).isEqualTo(foo.fullName());
   }
 
   @Test
   public void pushAccountConfigToUserBranchIsRejectedIfOwnAccountIsDeactivated() throws Exception {
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
+    fetch(allUsersRepo, RefNames.refsUsers(admin.id()) + ":userRef");
     allUsersRepo.reset("userRef");
 
     Config ac = getAccountConfig(allUsersRepo);
@@ -1724,7 +1728,7 @@
     PushOneCommit.Result r =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 allUsersRepo,
                 "Update account config",
                 AccountProperties.ACCOUNT_CONFIG,
@@ -1740,8 +1744,8 @@
     allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
 
     TestAccount foo = accountCreator.create(name("foo"));
-    assertThat(gApi.accounts().id(foo.id.get()).getActive()).isTrue();
-    String userRef = RefNames.refsUsers(foo.id);
+    assertThat(gApi.accounts().id(foo.id().get()).getActive()).isTrue();
+    String userRef = RefNames.refsUsers(foo.id());
     accountIndexedCounter.clear();
 
     grant(allUsers, userRef, Permission.PUSH, false, adminGroupUuid());
@@ -1755,7 +1759,7 @@
 
     pushFactory
         .create(
-            admin.getIdent(),
+            admin.newIdent(),
             allUsersRepo,
             "Update account config",
             AccountProperties.ACCOUNT_CONFIG,
@@ -1764,7 +1768,7 @@
         .assertOkStatus();
     accountIndexedCounter.assertReindexOf(foo);
 
-    assertThat(gApi.accounts().id(foo.id.get()).getActive()).isFalse();
+    assertThat(gApi.accounts().id(foo.id().get()).getActive()).isFalse();
   }
 
   @Test
@@ -1774,7 +1778,7 @@
 
     String userRef = RefNames.refsUsers(new Account.Id(seq.nextAccountId()));
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    PushOneCommit.Result r = pushFactory.create(admin.getIdent(), allUsersRepo).to(userRef);
+    PushOneCommit.Result r = pushFactory.create(admin.newIdent(), allUsersRepo).to(userRef);
     r.assertErrorStatus();
     assertThat(r.getMessage()).contains("Not allowed to create user branch.");
 
@@ -1791,7 +1795,7 @@
 
     String userRef = RefNames.refsUsers(new Account.Id(seq.nextAccountId()));
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    pushFactory.create(admin.getIdent(), allUsersRepo).to(userRef).assertOkStatus();
+    pushFactory.create(admin.newIdent(), allUsersRepo).to(userRef).assertOkStatus();
 
     try (Repository repo = repoManager.openRepository(allUsers)) {
       assertThat(repo.exactRef(userRef)).isNotNull();
@@ -1807,7 +1811,7 @@
 
     String userRef = RefNames.REFS_USERS + "foo";
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    PushOneCommit.Result r = pushFactory.create(admin.getIdent(), allUsersRepo).to(userRef);
+    PushOneCommit.Result r = pushFactory.create(admin.newIdent(), allUsersRepo).to(userRef);
     r.assertErrorStatus();
     assertThat(r.getMessage()).contains("Not allowed to create non-user branch under refs/users/.");
 
@@ -1827,7 +1831,7 @@
 
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
     pushFactory
-        .create(admin.getIdent(), allUsersRepo)
+        .create(admin.newIdent(), allUsersRepo)
         .to(RefNames.REFS_USERS_DEFAULT)
         .assertOkStatus();
 
@@ -1846,7 +1850,7 @@
         REGISTERED_USERS);
 
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    String userRef = RefNames.refsUsers(admin.id);
+    String userRef = RefNames.refsUsers(admin.id());
     PushResult r = deleteRef(allUsersRepo, userRef);
     RemoteRefUpdate refUpdate = r.getRemoteUpdate(userRef);
     assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.REJECTED_OTHER_REASON);
@@ -1868,7 +1872,7 @@
         REGISTERED_USERS);
 
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    String userRef = RefNames.refsUsers(admin.id);
+    String userRef = RefNames.refsUsers(admin.id());
     PushResult r = deleteRef(allUsersRepo, userRef);
     RemoteRefUpdate refUpdate = r.getRemoteUpdate(userRef);
     assertThat(refUpdate.getStatus()).isEqualTo(RemoteRefUpdate.Status.OK);
@@ -1877,8 +1881,8 @@
       assertThat(repo.exactRef(userRef)).isNull();
     }
 
-    assertThat(accountCache.get(admin.id)).isEmpty();
-    assertThat(accountQueryProvider.get().byDefault(admin.id.toString())).isEmpty();
+    assertThat(accountCache.get(admin.id())).isEmpty();
+    assertThat(accountQueryProvider.get().byDefault(admin.id().toString())).isEmpty();
   }
 
   @Test
@@ -1890,7 +1894,7 @@
     assertKeyMapContains(key, addGpgKey(key.getPublicKeyArmored()));
     assertKeys(key);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(ResourceNotFoundException.class);
     exception.expectMessage(id);
     gApi.accounts().self().gpgKey(id).get();
@@ -1921,13 +1925,13 @@
         .get()
         .update(
             "Add External ID",
-            user.getId(),
-            u -> u.addExternalId(ExternalId.create("foo", "myId", user.getId())));
+            user.id(),
+            u -> u.addExternalId(ExternalId.create("foo", "myId", user.id())));
     accountIndexedCounter.assertReindexOf(user);
 
     TestKey key = validKeyWithSecondUserId();
     addGpgKey(key.getPublicKeyArmored());
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     exception.expect(ResourceConflictException.class);
     exception.expectMessage("GPG key already associated with another account");
@@ -2020,12 +2024,12 @@
     assertSequenceNumbers(info);
     SshKeyInfo key = info.get(0);
     KeyPair keyPair = sshKeys.getKeyPair(admin);
-    String inital = TestSshKeys.publicKey(keyPair, admin.email);
+    String inital = TestSshKeys.publicKey(keyPair, admin.email());
     assertThat(key.sshPublicKey).isEqualTo(inital);
     accountIndexedCounter.assertNoReindex();
 
     // Add a new key
-    String newKey = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email);
+    String newKey = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email());
     gApi.accounts().self().addSshKey(newKey);
     info = gApi.accounts().self().listSshKeys();
     assertThat(info).hasSize(2);
@@ -2040,7 +2044,7 @@
     accountIndexedCounter.assertNoReindex();
 
     // Add another new key
-    String newKey2 = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email);
+    String newKey2 = TestSshKeys.publicKey(TestSshKeys.genSshKey(), admin.email());
     gApi.accounts().self().addSshKey(newKey2);
     info = gApi.accounts().self().listSshKeys();
     assertThat(info).hasSize(3);
@@ -2057,7 +2061,7 @@
 
     // Mark first key as invalid
     assertThat(info.get(0).valid).isTrue();
-    authorizedKeys.markKeyInvalid(admin.id, 1);
+    authorizedKeys.markKeyInvalid(admin.id(), 1);
     info = gApi.accounts().self().listSshKeys();
     assertThat(info).hasSize(2);
     assertThat(info.get(0).seq).isEqualTo(1);
@@ -2070,19 +2074,19 @@
   @Test
   public void reindexPermissions() throws Exception {
     // admin can reindex any account
-    requestScopeOperations.setApiUser(admin.getId());
-    gApi.accounts().id(user.username).index();
+    requestScopeOperations.setApiUser(admin.id());
+    gApi.accounts().id(user.username()).index();
     accountIndexedCounter.assertReindexOf(user);
 
     // user can reindex own account
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.accounts().self().index();
     accountIndexedCounter.assertReindexOf(user);
 
     // user cannot reindex any account
     exception.expect(AuthException.class);
     exception.expectMessage("modify account not permitted");
-    gApi.accounts().id(admin.username).index();
+    gApi.accounts().id(admin.username()).index();
   }
 
   @Test
@@ -2108,13 +2112,13 @@
         .get()
         .update(
             "Delete External ID",
-            account.getId(),
-            u -> u.deleteExternalId(ExternalId.createEmail(account.getId(), email)));
+            account.id(),
+            u -> u.deleteExternalId(ExternalId.createEmail(account.id(), email)));
     expectedProblems.add(
         new ConsistencyProblemInfo(
             ConsistencyProblemInfo.Status.ERROR,
             "Account '"
-                + account.getId().get()
+                + account.id().get()
                 + "' has no external ID for its preferred email '"
                 + email
                 + "'"));
@@ -2130,11 +2134,11 @@
     assertThat(accountQueryProvider.get().byDefault(name)).isEmpty();
 
     TestAccount foo1 = accountCreator.create(name + "-1");
-    assertThat(gApi.accounts().id(foo1.username).getActive()).isTrue();
+    assertThat(gApi.accounts().id(foo1.username()).getActive()).isTrue();
 
     TestAccount foo2 = accountCreator.create(name + "-2");
-    gApi.accounts().id(foo2.username).setActive(false);
-    assertThat(gApi.accounts().id(foo2.id.get()).getActive()).isFalse();
+    gApi.accounts().id(foo2.username()).setActive(false);
+    assertThat(gApi.accounts().id(foo2.id().get()).getActive()).isFalse();
 
     assertThat(accountQueryProvider.get().byDefault(name)).hasSize(2);
   }
@@ -2142,8 +2146,8 @@
   @Test
   public void checkMetaId() throws Exception {
     // metaId is set when account is loaded
-    assertThat(accounts.get(admin.getId()).get().getAccount().getMetaId())
-        .isEqualTo(getMetaId(admin.getId()));
+    assertThat(accounts.get(admin.id()).get().getAccount().getMetaId())
+        .isEqualTo(getMetaId(admin.id()));
 
     // metaId is set when account is created
     AccountsUpdate au = accountsUpdateProvider.get();
@@ -2182,7 +2186,7 @@
 
   @Test
   public void allGroupsForAnAdminAccountCanBeRetrieved() throws Exception {
-    List<GroupInfo> groups = gApi.accounts().id(admin.username).getGroups();
+    List<GroupInfo> groups = gApi.accounts().id(admin.username()).getGroups();
     assertThat(groups)
         .comparingElementsUsing(getGroupToNameCorrespondence())
         .containsExactly("Anonymous Users", "Registered Users", "Administrators");
@@ -2284,20 +2288,20 @@
                 try {
                   accountsUpdateProvider
                       .get()
-                      .update("Set Status", admin.id, u -> u.setStatus(status));
-                } catch (IOException | ConfigInvalidException | OrmException e) {
+                      .update("Set Status", admin.id(), u -> u.setStatus(status));
+                } catch (IOException | ConfigInvalidException | StorageException e) {
                   // Ignore, the successful update of the account is asserted later
                 }
               }
             },
             Runnables.doNothing());
     assertThat(doneBgUpdate.get()).isFalse();
-    AccountInfo accountInfo = gApi.accounts().id(admin.id.get()).get();
+    AccountInfo accountInfo = gApi.accounts().id(admin.id().get()).get();
     assertThat(accountInfo.status).isNull();
     assertThat(accountInfo.name).isNotEqualTo(fullName);
 
     Optional<AccountState> updatedAccountState =
-        update.update("Set Full Name", admin.id, u -> u.setFullName(fullName));
+        update.update("Set Full Name", admin.id(), u -> u.setFullName(fullName));
     assertThat(doneBgUpdate.get()).isTrue();
 
     assertThat(updatedAccountState).isPresent();
@@ -2305,7 +2309,7 @@
     assertThat(updatedAccount.getStatus()).isEqualTo(status);
     assertThat(updatedAccount.getFullName()).isEqualTo(fullName);
 
-    accountInfo = gApi.accounts().id(admin.id.get()).get();
+    accountInfo = gApi.accounts().id(admin.id().get()).get();
     assertThat(accountInfo.status).isEqualTo(status);
     assertThat(accountInfo.name).isEqualTo(fullName);
   }
@@ -2340,38 +2344,38 @@
                     .get()
                     .update(
                         "Set Status",
-                        admin.id,
+                        admin.id(),
                         u -> u.setStatus(status.get(bgCounter.getAndAdd(1))));
-              } catch (IOException | ConfigInvalidException | OrmException e) {
+              } catch (IOException | ConfigInvalidException | StorageException e) {
                 // Ignore, the expected exception is asserted later
               }
             },
             Runnables.doNothing());
     assertThat(bgCounter.get()).isEqualTo(0);
-    AccountInfo accountInfo = gApi.accounts().id(admin.id.get()).get();
+    AccountInfo accountInfo = gApi.accounts().id(admin.id().get()).get();
     assertThat(accountInfo.status).isNull();
     assertThat(accountInfo.name).isNotEqualTo(fullName);
 
     try {
-      update.update("Set Full Name", admin.id, u -> u.setFullName(fullName));
+      update.update("Set Full Name", admin.id(), u -> u.setFullName(fullName));
       fail("expected LockFailureException");
     } catch (LockFailureException e) {
       // Ignore, expected
     }
     assertThat(bgCounter.get()).isEqualTo(status.size());
 
-    Account updatedAccount = accounts.get(admin.id).get().getAccount();
+    Account updatedAccount = accounts.get(admin.id()).get().getAccount();
     assertThat(updatedAccount.getStatus()).isEqualTo(Iterables.getLast(status));
-    assertThat(updatedAccount.getFullName()).isEqualTo(admin.fullName);
+    assertThat(updatedAccount.getFullName()).isEqualTo(admin.fullName());
 
-    accountInfo = gApi.accounts().id(admin.id.get()).get();
+    accountInfo = gApi.accounts().id(admin.id().get()).get();
     assertThat(accountInfo.status).isEqualTo(Iterables.getLast(status));
-    assertThat(accountInfo.name).isEqualTo(admin.fullName);
+    assertThat(accountInfo.name).isEqualTo(admin.fullName());
   }
 
   @Test
   public void atomicReadMofifyWrite() throws Exception {
-    gApi.accounts().id(admin.id.get()).setStatus("A-1");
+    gApi.accounts().id(admin.id().get()).setStatus("A-1");
 
     AtomicInteger bgCounterA1 = new AtomicInteger(0);
     AtomicInteger bgCounterA2 = new AtomicInteger(0);
@@ -2394,19 +2398,19 @@
               try {
                 accountsUpdateProvider
                     .get()
-                    .update("Set Status", admin.id, u -> u.setStatus("A-2"));
-              } catch (IOException | ConfigInvalidException | OrmException e) {
+                    .update("Set Status", admin.id(), u -> u.setStatus("A-2"));
+              } catch (IOException | ConfigInvalidException | StorageException e) {
                 // Ignore, the expected exception is asserted later
               }
             });
     assertThat(bgCounterA1.get()).isEqualTo(0);
     assertThat(bgCounterA2.get()).isEqualTo(0);
-    assertThat(gApi.accounts().id(admin.id.get()).get().status).isEqualTo("A-1");
+    assertThat(gApi.accounts().id(admin.id().get()).get().status).isEqualTo("A-1");
 
     Optional<AccountState> updatedAccountState =
         update.update(
             "Set Status",
-            admin.id,
+            admin.id(),
             (a, u) -> {
               if ("A-1".equals(a.getAccount().getStatus())) {
                 bgCounterA1.getAndIncrement();
@@ -2424,8 +2428,8 @@
 
     assertThat(updatedAccountState).isPresent();
     assertThat(updatedAccountState.get().getAccount().getStatus()).isEqualTo("B-2");
-    assertThat(accounts.get(admin.id).get().getAccount().getStatus()).isEqualTo("B-2");
-    assertThat(gApi.accounts().id(admin.id.get()).get().status).isEqualTo("B-2");
+    assertThat(accounts.get(admin.id()).get().getAccount().getStatus()).isEqualTo("B-2");
+    assertThat(gApi.accounts().id(admin.id().get()).get().status).isEqualTo("B-2");
   }
 
   @Test
@@ -2464,7 +2468,7 @@
                         "Update External ID",
                         accountId,
                         u -> u.replaceExternalId(extIdA1, extIdA2));
-              } catch (IOException | ConfigInvalidException | OrmException e) {
+              } catch (IOException | ConfigInvalidException | StorageException e) {
                 // Ignore, the expected exception is asserted later
               }
             });
@@ -2605,7 +2609,7 @@
               null);
 
       // Create 2 drafts each on both changes for user.
-      requestScopeOperations.setApiUser(user.getId());
+      requestScopeOperations.setApiUser(user.id());
       createDraft(r1, PushOneCommit.FILE_NAME, "draft 1a");
       createDraft(r1, PushOneCommit.FILE_NAME, "draft 1b");
       createDraft(r2, PushOneCommit.FILE_NAME, "draft 2a");
@@ -2614,12 +2618,12 @@
       assertThat(gApi.changes().id(r2.getChangeId()).current().draftsAsList()).hasSize(2);
 
       // Create 1 draft on first change for admin.
-      requestScopeOperations.setApiUser(admin.getId());
+      requestScopeOperations.setApiUser(admin.id());
       createDraft(r1, PushOneCommit.FILE_NAME, "admin draft");
       assertThat(gApi.changes().id(r1.getChangeId()).current().draftsAsList()).hasSize(1);
 
       // Delete user's draft comments; leave admin's alone.
-      requestScopeOperations.setApiUser(user.getId());
+      requestScopeOperations.setApiUser(user.id());
       List<DeletedDraftCommentInfo> result =
           gApi.accounts().self().deleteDraftComments(new DeleteDraftCommentsInput());
 
@@ -2635,7 +2639,7 @@
       assertThat(gApi.changes().id(r1.getChangeId()).current().draftsAsList()).isEmpty();
       assertThat(gApi.changes().id(r2.getChangeId()).current().draftsAsList()).isEmpty();
 
-      requestScopeOperations.setApiUser(admin.getId());
+      requestScopeOperations.setApiUser(admin.id());
       assertThat(gApi.changes().id(r1.getChangeId()).current().draftsAsList()).hasSize(1);
     } finally {
       cleanUpDrafts();
@@ -2672,11 +2676,11 @@
   public void deleteOtherUsersDraftCommentsDisallowed() throws Exception {
     try {
       PushOneCommit.Result r = createChange();
-      requestScopeOperations.setApiUser(user.getId());
+      requestScopeOperations.setApiUser(user.id());
       createDraft(r, PushOneCommit.FILE_NAME, "draft");
-      requestScopeOperations.setApiUser(admin.getId());
+      requestScopeOperations.setApiUser(admin.id());
       try {
-        gApi.accounts().id(user.id.get()).deleteDraftComments(new DeleteDraftCommentsInput());
+        gApi.accounts().id(user.id().get()).deleteDraftComments(new DeleteDraftCommentsInput());
         assert_().fail("expected AuthException");
       } catch (AuthException e) {
         assertThat(e).hasMessageThat().isEqualTo("Cannot delete drafts of other user");
@@ -2693,7 +2697,7 @@
       PushOneCommit.Result r1 = createChange();
       PushOneCommit.Result r2 = createChange("refs/for/secret");
 
-      requestScopeOperations.setApiUser(user.getId());
+      requestScopeOperations.setApiUser(user.id());
       createDraft(r1, PushOneCommit.FILE_NAME, "draft a");
       createDraft(r2, PushOneCommit.FILE_NAME, "draft b");
       assertThat(gApi.changes().id(r1.getChangeId()).current().draftsAsList()).hasSize(1);
@@ -2725,7 +2729,7 @@
 
   private void cleanUpDrafts() throws Exception {
     for (TestAccount testAccount : accountCreator.getAll()) {
-      requestScopeOperations.setApiUser(testAccount.getId());
+      requestScopeOperations.setApiUser(testAccount.id());
       for (ChangeInfo changeInfo : gApi.changes().query("has:draft").get()) {
         for (CommentInfo c :
             gApi.changes().id(changeInfo.id).drafts().values().stream()
@@ -2738,18 +2742,15 @@
   }
 
   private static Correspondence<GroupInfo, String> getGroupToNameCorrespondence() {
-    return new Correspondence<GroupInfo, String>() {
-      @Override
-      public boolean compare(GroupInfo actualGroup, String expectedName) {
-        String groupName = actualGroup == null ? null : actualGroup.name;
-        return Objects.equals(groupName, expectedName);
-      }
-
-      @Override
-      public String toString() {
-        return "has name";
-      }
-    };
+    return Correspondence.from(
+        new BinaryPredicate<GroupInfo, String>() {
+          @Override
+          public boolean apply(GroupInfo actualGroup, String expectedName) {
+            String groupName = actualGroup == null ? null : actualGroup.name;
+            return Objects.equals(groupName, expectedName);
+          }
+        },
+        "has name");
   }
 
   private void assertSequenceNumbers(List<SshKeyInfo> sshKeys) {
@@ -2848,12 +2849,12 @@
         .get()
         .update(
             "Add Email",
-            account.getId(),
+            account.id(),
             u ->
                 u.addExternalId(
-                    ExternalId.createWithEmail(name("test"), email, account.getId(), email)));
+                    ExternalId.createWithEmail(name("test"), email, account.id(), email)));
     accountIndexedCounter.assertReindexOf(account);
-    requestScopeOperations.setApiUser(account.getId());
+    requestScopeOperations.setApiUser(account.id());
   }
 
   private Map<String, GpgKeyInfo> addGpgKey(String armored) throws Exception {
@@ -2873,9 +2874,9 @@
 
   private void assertUser(AccountInfo info, TestAccount account, @Nullable String expectedStatus)
       throws Exception {
-    assertThat(info.name).isEqualTo(account.fullName);
-    assertThat(info.email).isEqualTo(account.email);
-    assertThat(info.username).isEqualTo(account.username);
+    assertThat(info.name).isEqualTo(account.fullName());
+    assertThat(info.email).isEqualTo(account.email());
+    assertThat(info.username).isEqualTo(account.username());
     assertThat(info.status).isEqualTo(expectedStatus);
   }
 
@@ -2885,7 +2886,7 @@
 
   private void assertEmail(Set<Account.Id> accounts, TestAccount expectedAccount) {
     assertThat(accounts).hasSize(1);
-    assertThat(Iterables.getOnlyElement(accounts)).isEqualTo(expectedAccount.getId());
+    assertThat(Iterables.getOnlyElement(accounts)).isEqualTo(expectedAccount.id());
   }
 
   private Config getAccountConfig(TestRepository<?> allUsersRepo) throws Exception {
@@ -2934,7 +2935,7 @@
     }
 
     void assertReindexOf(TestAccount testAccount, int expectedCount) {
-      assertThat(getCount(testAccount.id)).isEqualTo(expectedCount);
+      assertThat(getCount(testAccount.id())).isEqualTo(expectedCount);
       assertThat(countsByAccount).hasSize(1);
       clear();
     }
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
index 29d4aa0..a4a5745 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AgreementsIT.java
@@ -125,7 +125,7 @@
   public void setUp() throws Exception {
     caAutoVerify = configureContributorAgreement(true);
     caNoAutoVerify = configureContributorAgreement(false);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
   }
 
   @Test
@@ -170,7 +170,7 @@
     gApi.accounts().self().signAgreement(caAutoVerify.getName());
 
     // Explicitly reset the user to force a new request context
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     // Verify that the agreement was signed
     result = gApi.accounts().self().listAgreements();
@@ -226,12 +226,12 @@
     ChangeInfo change = gApi.changes().create(newChangeInput()).get();
 
     // Approve and submit it
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(change.changeId).current().review(ReviewInput.approve());
     gApi.changes().id(change.changeId).current().submit(new SubmitInput());
 
     // Revert is not allowed when CLA is required but not signed
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     setUseContributorAgreements(InheritableBoolean.TRUE);
     exception.expect(AuthException.class);
     exception.expectMessage("Contributor Agreement");
@@ -250,12 +250,12 @@
     ChangeInfo change = gApi.changes().create(newChangeInput()).get();
 
     // Approve and submit it
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(change.changeId).current().review(ReviewInput.approve());
     gApi.changes().id(change.changeId).current().submit(new SubmitInput());
 
     // Revert in excluded project is allowed even when CLA is required but not signed
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     setUseContributorAgreements(InheritableBoolean.TRUE);
     gApi.changes().id(change.changeId).revert();
   }
@@ -265,7 +265,7 @@
     assume().that(isContributorAgreementsEnabled()).isTrue();
 
     // Create a new branch
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     BranchInfo dest =
         gApi.projects()
             .name(project.get())
@@ -282,7 +282,7 @@
     gApi.changes().id(change.changeId).current().submit(new SubmitInput());
 
     // Cherry-pick is not allowed when CLA is required but not signed
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     setUseContributorAgreements(InheritableBoolean.TRUE);
     CherryPickInput in = new CherryPickInput();
     in.destination = dest.ref;
@@ -313,7 +313,7 @@
     gApi.accounts().self().signAgreement(caAutoVerify.getName());
 
     // Explicitly reset the user to force a new request context
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     // Create a change succeeds after signing the agreement
     gApi.changes().create(newChangeInput());
@@ -360,7 +360,7 @@
   public void publishEditRestWithoutCLA() throws Exception {
     String filename = "foo";
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), testRepo, "subject1", filename, "contentold");
+        pushFactory.create(admin.newIdent(), testRepo, "subject1", filename, "contentold");
     PushOneCommit.Result result = push.to("refs/for/master");
     result.assertOkStatus();
     String changeId = result.getChangeId();
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java
index ba340eb..d7e765b 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/DiffPreferencesIT.java
@@ -29,7 +29,7 @@
   @Test
   public void getDiffPreferences() throws Exception {
     DiffPreferencesInfo d = DiffPreferencesInfo.defaults();
-    DiffPreferencesInfo o = gApi.accounts().id(admin.getId().toString()).getDiffPreferences();
+    DiffPreferencesInfo o = gApi.accounts().id(admin.id().toString()).getDiffPreferences();
     assertPrefs(o, d);
   }
 
@@ -64,13 +64,13 @@
     i.matchBrackets ^= true;
     i.lineWrapping ^= true;
 
-    DiffPreferencesInfo o = gApi.accounts().id(admin.getId().toString()).setDiffPreferences(i);
+    DiffPreferencesInfo o = gApi.accounts().id(admin.id().toString()).setDiffPreferences(i);
     assertPrefs(o, i);
 
     // Partially fill input record
     i = new DiffPreferencesInfo();
     i.tabSize = 42;
-    DiffPreferencesInfo a = gApi.accounts().id(admin.getId().toString()).setDiffPreferences(i);
+    DiffPreferencesInfo a = gApi.accounts().id(admin.id().toString()).setDiffPreferences(i);
     assertPrefs(a, o, "tabSize");
     assertThat(a.tabSize).isEqualTo(42);
   }
@@ -87,7 +87,7 @@
     update.fontSize = newFontSize;
     gApi.config().server().setDefaultDiffPreferences(update);
 
-    DiffPreferencesInfo o = gApi.accounts().id(admin.getId().toString()).getDiffPreferences();
+    DiffPreferencesInfo o = gApi.accounts().id(admin.id().toString()).getDiffPreferences();
 
     // assert configured defaults
     assertThat(o.lineLength).isEqualTo(newLineLength);
@@ -106,29 +106,29 @@
     update.lineLength = configuredDefaultLineLength;
     gApi.config().server().setDefaultDiffPreferences(update);
 
-    DiffPreferencesInfo o = gApi.accounts().id(admin.getId().toString()).getDiffPreferences();
+    DiffPreferencesInfo o = gApi.accounts().id(admin.id().toString()).getDiffPreferences();
     assertThat(o.lineLength).isEqualTo(configuredDefaultLineLength);
     assertPrefs(o, d, "lineLength");
 
     int newLineLength = configuredDefaultLineLength + 10;
     DiffPreferencesInfo i = new DiffPreferencesInfo();
     i.lineLength = newLineLength;
-    DiffPreferencesInfo a = gApi.accounts().id(admin.getId().toString()).setDiffPreferences(i);
+    DiffPreferencesInfo a = gApi.accounts().id(admin.id().toString()).setDiffPreferences(i);
     assertThat(a.lineLength).isEqualTo(newLineLength);
     assertPrefs(a, d, "lineLength");
 
-    a = gApi.accounts().id(admin.getId().toString()).getDiffPreferences();
+    a = gApi.accounts().id(admin.id().toString()).getDiffPreferences();
     assertThat(a.lineLength).isEqualTo(newLineLength);
     assertPrefs(a, d, "lineLength");
 
     // overwrite the configured default with original hard-coded default
     i = new DiffPreferencesInfo();
     i.lineLength = d.lineLength;
-    a = gApi.accounts().id(admin.getId().toString()).setDiffPreferences(i);
+    a = gApi.accounts().id(admin.id().toString()).setDiffPreferences(i);
     assertThat(a.lineLength).isEqualTo(d.lineLength);
     assertPrefs(a, d, "lineLength");
 
-    a = gApi.accounts().id(admin.getId().toString()).getDiffPreferences();
+    a = gApi.accounts().id(admin.id().toString()).getDiffPreferences();
     assertThat(a.lineLength).isEqualTo(d.lineLength);
     assertPrefs(a, d, "lineLength");
   }
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
index c1d9bcb..00d1733 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/EditPreferencesIT.java
@@ -27,7 +27,7 @@
 public class EditPreferencesIT extends AbstractDaemonTest {
   @Test
   public void getSetEditPreferences() throws Exception {
-    EditPreferencesInfo out = gApi.accounts().id(admin.getId().toString()).getEditPreferences();
+    EditPreferencesInfo out = gApi.accounts().id(admin.id().toString()).getEditPreferences();
 
     assertThat(out.lineLength).isEqualTo(100);
     assertThat(out.indentUnit).isEqualTo(2);
@@ -64,7 +64,7 @@
     out.theme = Theme.TWILIGHT;
     out.keyMapType = KeyMapType.EMACS;
 
-    EditPreferencesInfo info = gApi.accounts().id(admin.getId().toString()).setEditPreferences(out);
+    EditPreferencesInfo info = gApi.accounts().id(admin.id().toString()).setEditPreferences(out);
 
     assertEditPreferences(info, out);
 
@@ -72,7 +72,7 @@
     EditPreferencesInfo in = new EditPreferencesInfo();
     in.tabSize = 42;
 
-    info = gApi.accounts().id(admin.getId().toString()).setEditPreferences(in);
+    info = gApi.accounts().id(admin.id().toString()).setEditPreferences(in);
 
     out.tabSize = in.tabSize;
     assertEditPreferences(info, out);
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
index 24040a4..70e37ef 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/GeneralPreferencesIT.java
@@ -56,7 +56,7 @@
 
   @Test
   public void getAndSetPreferences() throws Exception {
-    GeneralPreferencesInfo o = gApi.accounts().id(user42.id.toString()).getPreferences();
+    GeneralPreferencesInfo o = gApi.accounts().id(user42.id().toString()).getPreferences();
     assertPrefs(o, GeneralPreferencesInfo.defaults(), "my", "changeTable");
     assertThat(o.my)
         .containsExactly(
@@ -96,7 +96,7 @@
     i.urlAliases = new HashMap<>();
     i.urlAliases.put("foo", "bar");
 
-    o = gApi.accounts().id(user42.getId().toString()).setPreferences(i);
+    o = gApi.accounts().id(user42.id().toString()).setPreferences(i);
     assertPrefs(o, i, "my");
     assertThat(o.my).containsExactlyElementsIn(i.my);
     assertThat(o.changeTable).containsExactlyElementsIn(i.changeTable);
@@ -110,7 +110,7 @@
     update.changesPerPage = newChangesPerPage;
     gApi.config().server().setDefaultPreferences(update);
 
-    GeneralPreferencesInfo o = gApi.accounts().id(user42.getId().toString()).getPreferences();
+    GeneralPreferencesInfo o = gApi.accounts().id(user42.id().toString()).getPreferences();
 
     // assert configured defaults
     assertThat(o.changesPerPage).isEqualTo(newChangesPerPage);
@@ -127,29 +127,29 @@
     update.changesPerPage = configuredChangesPerPage;
     gApi.config().server().setDefaultPreferences(update);
 
-    GeneralPreferencesInfo o = gApi.accounts().id(admin.getId().toString()).getPreferences();
+    GeneralPreferencesInfo o = gApi.accounts().id(admin.id().toString()).getPreferences();
     assertThat(o.changesPerPage).isEqualTo(configuredChangesPerPage);
     assertPrefs(o, d, "my", "changeTable", "changesPerPage");
 
     int newChangesPerPage = configuredChangesPerPage * 2;
     GeneralPreferencesInfo i = new GeneralPreferencesInfo();
     i.changesPerPage = newChangesPerPage;
-    GeneralPreferencesInfo a = gApi.accounts().id(admin.getId().toString()).setPreferences(i);
+    GeneralPreferencesInfo a = gApi.accounts().id(admin.id().toString()).setPreferences(i);
     assertThat(a.changesPerPage).isEqualTo(newChangesPerPage);
     assertPrefs(a, d, "my", "changeTable", "changesPerPage");
 
-    a = gApi.accounts().id(admin.getId().toString()).getPreferences();
+    a = gApi.accounts().id(admin.id().toString()).getPreferences();
     assertThat(a.changesPerPage).isEqualTo(newChangesPerPage);
     assertPrefs(a, d, "my", "changeTable", "changesPerPage");
 
     // overwrite the configured default with original hard-coded default
     i = new GeneralPreferencesInfo();
     i.changesPerPage = d.changesPerPage;
-    a = gApi.accounts().id(admin.getId().toString()).setPreferences(i);
+    a = gApi.accounts().id(admin.id().toString()).setPreferences(i);
     assertThat(a.changesPerPage).isEqualTo(d.changesPerPage);
     assertPrefs(a, d, "my", "changeTable", "changesPerPage");
 
-    a = gApi.accounts().id(admin.getId().toString()).getPreferences();
+    a = gApi.accounts().id(admin.id().toString()).getPreferences();
     assertThat(a.changesPerPage).isEqualTo(d.changesPerPage);
     assertPrefs(a, d, "my", "changeTable", "changesPerPage");
   }
@@ -162,7 +162,7 @@
 
     exception.expect(BadRequestException.class);
     exception.expectMessage("name for menu item is required");
-    gApi.accounts().id(user42.getId().toString()).setPreferences(i);
+    gApi.accounts().id(user42.id().toString()).setPreferences(i);
   }
 
   @Test
@@ -173,7 +173,7 @@
 
     exception.expect(BadRequestException.class);
     exception.expectMessage("URL for menu item is required");
-    gApi.accounts().id(user42.getId().toString()).setPreferences(i);
+    gApi.accounts().id(user42.id().toString()).setPreferences(i);
   }
 
   @Test
@@ -182,7 +182,7 @@
     i.my = new ArrayList<>();
     i.my.add(new MenuItem(" name\t", " url\t", " _blank\t", " id\t"));
 
-    GeneralPreferencesInfo o = gApi.accounts().id(user42.getId().toString()).setPreferences(i);
+    GeneralPreferencesInfo o = gApi.accounts().id(user42.id().toString()).setPreferences(i);
     assertThat(o.my).containsExactly(new MenuItem("name", "url", "_blank", "id"));
   }
 
@@ -193,7 +193,7 @@
 
     exception.expect(BadRequestException.class);
     exception.expectMessage("Unsupported download scheme: " + i.downloadScheme);
-    gApi.accounts().id(user42.getId().toString()).setPreferences(i);
+    gApi.accounts().id(user42.id().toString()).setPreferences(i);
   }
 
   @Test
@@ -206,10 +206,10 @@
       GeneralPreferencesInfo i = GeneralPreferencesInfo.defaults();
       i.downloadScheme = schemeName;
 
-      GeneralPreferencesInfo o = gApi.accounts().id(user42.getId().toString()).setPreferences(i);
+      GeneralPreferencesInfo o = gApi.accounts().id(user42.id().toString()).setPreferences(i);
       assertThat(o.downloadScheme).isEqualTo(schemeName);
 
-      o = gApi.accounts().id(user42.getId().toString()).getPreferences();
+      o = gApi.accounts().id(user42.id().toString()).getPreferences();
       assertThat(o.downloadScheme).isEqualTo(schemeName);
     } finally {
       registrationHandle.remove();
@@ -222,7 +222,7 @@
     // becomes unsupported.
     setDownloadScheme();
 
-    GeneralPreferencesInfo o = gApi.accounts().id(user42.getId().toString()).getPreferences();
+    GeneralPreferencesInfo o = gApi.accounts().id(user42.id().toString()).getPreferences();
     assertThat(o.downloadScheme).isNull();
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java b/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java
index 691dd94..925c66a 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/AbandonIT.java
@@ -156,7 +156,7 @@
     PushOneCommit.Result r = createChange();
     String changeId = r.getChangeId();
     assertThat(info(changeId).status).isEqualTo(ChangeStatus.NEW);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("abandon not permitted");
     gApi.changes().id(changeId).abandon();
@@ -168,7 +168,7 @@
     String changeId = r.getChangeId();
     assertThat(info(changeId).status).isEqualTo(ChangeStatus.NEW);
     grant(project, "refs/heads/master", Permission.ABANDON, false, REGISTERED_USERS);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(changeId).abandon();
     assertThat(info(changeId).status).isEqualTo(ChangeStatus.ABANDONED);
     gApi.changes().id(changeId).restore();
@@ -199,7 +199,7 @@
     String changeId = r.getChangeId();
     assertThat(info(changeId).status).isEqualTo(ChangeStatus.NEW);
     gApi.changes().id(changeId).abandon();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertThat(info(changeId).status).isEqualTo(ChangeStatus.ABANDONED);
     exception.expect(AuthException.class);
     exception.expectMessage("restore not permitted");
diff --git a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
index e638e9c..ebb3978 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -57,6 +57,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
+import com.google.common.truth.ThrowableSubject;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.ChangeIndexedCounter;
 import com.google.gerrit.acceptance.GerritConfig;
@@ -73,6 +74,7 @@
 import com.google.gerrit.common.data.LabelFunction;
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.extensions.annotations.Exports;
 import com.google.gerrit.extensions.api.changes.AddReviewerInput;
 import com.google.gerrit.extensions.api.changes.AddReviewerResult;
 import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
@@ -129,6 +131,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
 import com.google.gerrit.index.IndexConfig;
+import com.google.gerrit.index.query.PostFilterPredicate;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
@@ -147,6 +150,7 @@
 import com.google.gerrit.server.index.change.IndexedChangeQuery;
 import com.google.gerrit.server.project.testing.Util;
 import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.ChangeQueryBuilder.ChangeOperatorFactory;
 import com.google.gerrit.server.restapi.change.PostReview;
 import com.google.gerrit.server.update.BatchUpdate;
 import com.google.gerrit.server.update.BatchUpdateOp;
@@ -154,6 +158,7 @@
 import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.gerrit.testing.FakeEmailSender.Message;
 import com.google.gerrit.testing.TestTimeUtil;
+import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
 import java.io.IOException;
 import java.sql.Timestamp;
@@ -164,6 +169,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
@@ -236,7 +242,7 @@
     assertThat(c.created).isEqualTo(c.updated);
     assertThat(c._number).isEqualTo(r.getChange().getId().get());
 
-    assertThat(c.owner._accountId).isEqualTo(admin.getId().get());
+    assertThat(c.owner._accountId).isEqualTo(admin.id().get());
     assertThat(c.owner.name).isNull();
     assertThat(c.owner.email).isNull();
     assertThat(c.owner.username).isNull();
@@ -268,7 +274,7 @@
     PushOneCommit.Result rwip = createChange();
     String changeId = rwip.getChangeId();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("toggle work in progress state not permitted");
     gApi.changes().id(changeId).setWorkInProgress();
@@ -276,24 +282,24 @@
 
   @Test
   public void setWorkInProgressAllowedAsAdmin() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     String changeId =
         gApi.changes().create(new ChangeInput(project.get(), "master", "Test Change")).get().id;
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(changeId).setWorkInProgress();
     assertThat(gApi.changes().id(changeId).get().workInProgress).isTrue();
   }
 
   @Test
   public void setWorkInProgressAllowedAsProjectOwner() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     String changeId =
         gApi.changes().create(new ChangeInput(project.get(), "master", "Test Change")).get().id;
 
     com.google.gerrit.acceptance.TestAccount user2 = accountCreator.user2();
     grant(project, "refs/*", Permission.OWNER, false, REGISTERED_USERS);
-    requestScopeOperations.setApiUser(user2.getId());
+    requestScopeOperations.setApiUser(user2.id());
     gApi.changes().id(changeId).setWorkInProgress();
     assertThat(gApi.changes().id(changeId).get().workInProgress).isTrue();
   }
@@ -314,7 +320,7 @@
     String changeId = rready.getChangeId();
     gApi.changes().id(changeId).setWorkInProgress();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("toggle work in progress state not permitted");
     gApi.changes().id(changeId).setReadyForReview();
@@ -322,26 +328,26 @@
 
   @Test
   public void setReadyForReviewAllowedAsAdmin() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     String changeId =
         gApi.changes().create(new ChangeInput(project.get(), "master", "Test Change")).get().id;
     gApi.changes().id(changeId).setWorkInProgress();
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(changeId).setReadyForReview();
     assertThat(gApi.changes().id(changeId).get().workInProgress).isNull();
   }
 
   @Test
   public void setReadyForReviewAllowedAsProjectOwner() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     String changeId =
         gApi.changes().create(new ChangeInput(project.get(), "master", "Test Change")).get().id;
     gApi.changes().id(changeId).setWorkInProgress();
 
     com.google.gerrit.acceptance.TestAccount user2 = accountCreator.user2();
     grant(project, "refs/*", Permission.OWNER, false, REGISTERED_USERS);
-    requestScopeOperations.setApiUser(user2.getId());
+    requestScopeOperations.setApiUser(user2.id());
     gApi.changes().id(changeId).setReadyForReview();
     assertThat(gApi.changes().id(changeId).get().workInProgress).isNull();
   }
@@ -414,7 +420,7 @@
         ais -> ais.stream().map(ai -> ai.email).collect(toSet());
     assertThat(toEmails.apply(info.pendingReviewers.get(REVIEWER)))
         .containsExactly(
-            admin.email, email1, email2, "byemail1@example.com", "byemail2@example.com");
+            admin.email(), email1, email2, "byemail1@example.com", "byemail2@example.com");
     assertThat(toEmails.apply(info.pendingReviewers.get(CC)))
         .containsExactly(email3, email4, "byemail3@example.com", "byemail4@example.com");
     assertThat(info.pendingReviewers.get(REMOVED)).isNull();
@@ -426,7 +432,7 @@
     gApi.changes().id(changeId).reviewer("byemail3@example.com").remove();
     info = gApi.changes().id(changeId).get();
     assertThat(toEmails.apply(info.pendingReviewers.get(REVIEWER)))
-        .containsExactly(admin.email, email2, "byemail2@example.com");
+        .containsExactly(admin.email(), email2, "byemail2@example.com");
     assertThat(toEmails.apply(info.pendingReviewers.get(CC)))
         .containsExactly(email4, "byemail4@example.com");
     assertThat(toEmails.apply(info.pendingReviewers.get(REMOVED)))
@@ -437,7 +443,7 @@
     gApi.changes().id(changeId).revision("current").review(in);
     info = gApi.changes().id(changeId).get();
     assertThat(toEmails.apply(info.pendingReviewers.get(REVIEWER)))
-        .containsExactly(admin.email, email1, email2, "byemail2@example.com");
+        .containsExactly(admin.email(), email1, email2, "byemail2@example.com");
     assertThat(toEmails.apply(info.pendingReviewers.get(CC)))
         .containsExactly(email4, "byemail4@example.com");
     assertThat(toEmails.apply(info.pendingReviewers.get(REMOVED)))
@@ -448,7 +454,7 @@
     info = gApi.changes().id(changeId).get();
     assertThat(info.pendingReviewers).isEmpty();
     assertThat(toEmails.apply(info.reviewers.get(REVIEWER)))
-        .containsExactly(admin.email, email1, email2, "byemail2@example.com");
+        .containsExactly(admin.email(), email1, email2, "byemail2@example.com");
     assertThat(toEmails.apply(info.reviewers.get(CC)))
         .containsExactly(email4, "byemail4@example.com");
     assertThat(info.reviewers.get(REMOVED)).isNull();
@@ -506,7 +512,7 @@
         false,
         REGISTERED_USERS);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(changeId).setWorkInProgress(refactor);
 
     ChangeInfo info = gApi.changes().id(changeId).get();
@@ -556,14 +562,17 @@
     assertThat(r.getChange().change().isWorkInProgress()).isFalse();
 
     ReviewInput in =
-        ReviewInput.approve().reviewer(user.email).label("Code-Review", 1).setWorkInProgress(true);
+        ReviewInput.approve()
+            .reviewer(user.email())
+            .label("Code-Review", 1)
+            .setWorkInProgress(true);
     gApi.changes().id(r.getChangeId()).revision("current").review(in);
 
     ChangeInfo info = gApi.changes().id(r.getChangeId()).get();
     assertThat(info.workInProgress).isTrue();
     assertThat(info.reviewers.get(REVIEWER).stream().map(ai -> ai._accountId).collect(toList()))
-        .containsExactly(admin.id.get(), user.id.get());
-    assertThat(info.labels.get("Code-Review").recommended._accountId).isEqualTo(admin.id.get());
+        .containsExactly(admin.id().get(), user.id().get());
+    assertThat(info.labels.get("Code-Review").recommended._accountId).isEqualTo(admin.id().get());
   }
 
   @Test
@@ -579,12 +588,12 @@
   @Test
   @TestProjectInput(cloneAs = "user")
   public void reviewWithWorkInProgressChangeOwner() throws Exception {
-    PushOneCommit push = pushFactory.create(user.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(user.newIdent(), testRepo);
     PushOneCommit.Result r = push.to("refs/for/master");
     r.assertOkStatus();
-    assertThat(r.getChange().change().getOwner()).isEqualTo(user.id);
+    assertThat(r.getChange().change().getOwner()).isEqualTo(user.id());
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     ReviewInput in = ReviewInput.noScore().setWorkInProgress(true);
     gApi.changes().id(r.getChangeId()).current().review(in);
     ChangeInfo info = gApi.changes().id(r.getChangeId()).get();
@@ -594,12 +603,12 @@
   @Test
   @TestProjectInput(cloneAs = "user")
   public void reviewWithWithWorkInProgressAdmin() throws Exception {
-    PushOneCommit push = pushFactory.create(user.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(user.newIdent(), testRepo);
     PushOneCommit.Result r = push.to("refs/for/master");
     r.assertOkStatus();
-    assertThat(r.getChange().change().getOwner()).isEqualTo(user.id);
+    assertThat(r.getChange().change().getOwner()).isEqualTo(user.id());
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     ReviewInput in = ReviewInput.noScore().setWorkInProgress(true);
     gApi.changes().id(r.getChangeId()).current().review(in);
     ChangeInfo info = gApi.changes().id(r.getChangeId()).get();
@@ -610,7 +619,7 @@
   public void reviewWithWorkInProgressByNonOwnerReturnsError() throws Exception {
     PushOneCommit.Result r = createChange();
     ReviewInput in = ReviewInput.noScore().setWorkInProgress(true);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("toggle work in progress state not permitted");
     gApi.changes().id(r.getChangeId()).current().review(in);
@@ -626,7 +635,7 @@
         Permission.TOGGLE_WORK_IN_PROGRESS_STATE,
         false,
         REGISTERED_USERS);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(r.getChangeId()).current().review(in);
     ChangeInfo info = gApi.changes().id(r.getChangeId()).get();
     assertThat(info.workInProgress).isTrue();
@@ -636,7 +645,7 @@
   public void reviewWithReadyByNonOwnerReturnsError() throws Exception {
     PushOneCommit.Result r = createChange();
     ReviewInput in = ReviewInput.noScore().setReady(true);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("toggle work in progress state not permitted");
     gApi.changes().id(r.getChangeId()).current().review(in);
@@ -654,7 +663,7 @@
 
     PushOneCommit push2 =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             PushOneCommit.FILE_NAME,
@@ -695,7 +704,7 @@
   @Test
   public void revertNotifications() throws Exception {
     PushOneCommit.Result r = createChange();
-    gApi.changes().id(r.getChangeId()).addReviewer(user.email);
+    gApi.changes().id(r.getChangeId()).addReviewer(user.email());
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
 
@@ -711,7 +720,7 @@
   @Test
   public void suppressRevertNotifications() throws Exception {
     PushOneCommit.Result r = createChange();
-    gApi.changes().id(r.getChangeId()).addReviewer(user.email);
+    gApi.changes().id(r.getChangeId()).addReviewer(user.email());
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
 
@@ -728,17 +737,17 @@
     PushOneCommit.Result r = createChange();
 
     ReviewInput in = ReviewInput.approve();
-    in.reviewer(user.email);
-    in.reviewer(accountCreator.user2().email, ReviewerState.CC, true);
+    in.reviewer(user.email());
+    in.reviewer(accountCreator.user2().email(), ReviewerState.CC, true);
     // Add user as reviewer that will create the revert
-    in.reviewer(accountCreator.admin2().email);
+    in.reviewer(accountCreator.admin2().email());
 
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(in);
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
 
     // expect both the original reviewers and CCs to be preserved
     // original owner should be added as reviewer, user requesting the revert (new owner) removed
-    requestScopeOperations.setApiUser(accountCreator.admin2().getId());
+    requestScopeOperations.setApiUser(accountCreator.admin2().id());
     Map<ReviewerState, Collection<AccountInfo>> result =
         gApi.changes().id(r.getChangeId()).revert().get().reviewers;
     assertThat(result).containsKey(ReviewerState.REVIEWER);
@@ -748,8 +757,8 @@
     assertThat(result).containsKey(ReviewerState.CC);
     List<Integer> ccs =
         result.get(ReviewerState.CC).stream().map(a -> a._accountId).collect(toList());
-    assertThat(ccs).containsExactly(accountCreator.user2().id.get());
-    assertThat(reviewers).containsExactly(user.id.get(), admin.id.get());
+    assertThat(ccs).containsExactly(accountCreator.user2().id().get());
+    assertThat(reviewers).containsExactly(user.id().get(), admin.id().get());
   }
 
   @Test
@@ -804,8 +813,8 @@
     // ...and the committer and description should be correct
     ChangeInfo info = gApi.changes().id(changeId).get(CURRENT_REVISION, CURRENT_COMMIT);
     GitPerson committer = info.revisions.get(info.currentRevision).commit.committer;
-    assertThat(committer.name).isEqualTo(admin.fullName);
-    assertThat(committer.email).isEqualTo(admin.email);
+    assertThat(committer.name).isEqualTo(admin.fullName());
+    assertThat(committer.email).isEqualTo(admin.email());
     String description = info.revisions.get(info.currentRevision).description;
     assertThat(description).isEqualTo("Rebase");
 
@@ -920,7 +929,7 @@
 
     // Rebase the second
     String changeId = r2.getChangeId();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("rebase not permitted");
     gApi.changes().id(changeId).rebase();
@@ -942,7 +951,7 @@
 
     // Rebase the second
     String changeId = r2.getChangeId();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(changeId).rebase();
   }
 
@@ -963,7 +972,7 @@
 
     // Rebase the second
     String changeId = r2.getChangeId();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("rebase not permitted");
     gApi.changes().id(changeId).rebase();
@@ -999,10 +1008,10 @@
   @TestProjectInput(cloneAs = "user")
   public void deleteNewChangeAsNormalUser() throws Exception {
     PushOneCommit.Result changeResult =
-        pushFactory.create(user.getIdent(), testRepo).to("refs/for/master");
+        pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
     String changeId = changeResult.getChangeId();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("delete not permitted");
     gApi.changes().id(changeId).delete();
@@ -1061,7 +1070,7 @@
       com.google.gerrit.acceptance.TestAccount deleteAs)
       throws Exception {
     try {
-      requestScopeOperations.setApiUser(owner.getId());
+      requestScopeOperations.setApiUser(owner.id());
       ChangeInput in = new ChangeInput();
       in.project = projectName.get();
       in.branch = "refs/heads/master";
@@ -1071,16 +1080,16 @@
       int id = changeInfo._number;
       String commit = changeInfo.currentRevision;
 
-      assertThat(gApi.changes().id(changeId).info().owner._accountId).isEqualTo(owner.id.get());
+      assertThat(gApi.changes().id(changeId).info().owner._accountId).isEqualTo(owner.id().get());
 
-      requestScopeOperations.setApiUser(deleteAs.getId());
+      requestScopeOperations.setApiUser(deleteAs.id());
       gApi.changes().id(changeId).delete();
 
       assertThat(query(changeId)).isEmpty();
 
       String ref = new Change.Id(id).toRefPrefix() + "1";
       eventRecorder.assertRefUpdatedEvents(projectName.get(), ref, null, commit, commit, null);
-      eventRecorder.assertChangeDeletedEvents(changeId, deleteAs.email);
+      eventRecorder.assertChangeDeletedEvents(changeId, deleteAs.email());
     } finally {
       removePermission(project, "refs/*", Permission.DELETE_OWN_CHANGES);
       removePermission(project, "refs/*", Permission.DELETE_CHANGES);
@@ -1100,7 +1109,7 @@
       PushOneCommit.Result changeResult = createChange();
       String changeId = changeResult.getChangeId();
 
-      requestScopeOperations.setApiUser(user.getId());
+      requestScopeOperations.setApiUser(user.id());
       exception.expect(AuthException.class);
       exception.expectMessage("delete not permitted");
       gApi.changes().id(changeId).delete();
@@ -1124,10 +1133,10 @@
   @TestProjectInput(cloneAs = "user")
   public void deleteAbandonedChangeAsNormalUser() throws Exception {
     PushOneCommit.Result changeResult =
-        pushFactory.create(user.getIdent(), testRepo).to("refs/for/master");
+        pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
     String changeId = changeResult.getChangeId();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(changeId).abandon();
 
     exception.expect(AuthException.class);
@@ -1139,7 +1148,7 @@
   @TestProjectInput(cloneAs = "user")
   public void deleteAbandonedChangeOfAnotherUserAsAdmin() throws Exception {
     PushOneCommit.Result changeResult =
-        pushFactory.create(user.getIdent(), testRepo).to("refs/for/master");
+        pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
     String changeId = changeResult.getChangeId();
 
     gApi.changes().id(changeId).abandon();
@@ -1168,12 +1177,12 @@
 
     try {
       PushOneCommit.Result changeResult =
-          pushFactory.create(user.getIdent(), testRepo).to("refs/for/master");
+          pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
       String changeId = changeResult.getChangeId();
 
       merge(changeResult);
 
-      requestScopeOperations.setApiUser(user.getId());
+      requestScopeOperations.setApiUser(user.id());
       exception.expect(MethodNotAllowedException.class);
       exception.expectMessage("delete not permitted");
       gApi.changes().id(changeId).delete();
@@ -1218,7 +1227,7 @@
   public void deleteChangeRemovesDraftComment() throws Exception {
     PushOneCommit.Result r = createChange();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     DraftInput dri = new DraftInput();
     dri.message = "hello";
@@ -1229,17 +1238,15 @@
     Change.Id num = r.getChange().getId();
 
     try (Repository repo = repoManager.openRepository(allUsers)) {
-      assertThat(
-              repo.getRefDatabase().getRefsByPrefix(RefNames.refsDraftComments(num, user.getId())))
+      assertThat(repo.getRefDatabase().getRefsByPrefix(RefNames.refsDraftComments(num, user.id())))
           .isNotEmpty();
     }
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
 
     gApi.changes().id(r.getChangeId()).delete();
     try (Repository repo = repoManager.openRepository(allUsers)) {
-      assertThat(
-              repo.getRefDatabase().getRefsByPrefix(RefNames.refsDraftComments(num, user.getId())))
+      assertThat(repo.getRefDatabase().getRefsByPrefix(RefNames.refsDraftComments(num, user.id())))
           .isEmpty();
     }
   }
@@ -1260,7 +1267,7 @@
 
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             PushOneCommit.FILE_NAME,
@@ -1416,31 +1423,31 @@
   @Test
   public void pushCommitOfOtherUser() throws Exception {
     // admin pushes commit of user
-    PushOneCommit push = pushFactory.create(user.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(user.newIdent(), testRepo);
     PushOneCommit.Result result = push.to("refs/for/master");
     result.assertOkStatus();
 
     ChangeInfo change = gApi.changes().id(result.getChangeId()).get();
-    assertThat(change.owner._accountId).isEqualTo(admin.id.get());
+    assertThat(change.owner._accountId).isEqualTo(admin.id().get());
     CommitInfo commit = change.revisions.get(change.currentRevision).commit;
-    assertThat(commit.author.email).isEqualTo(user.email);
-    assertThat(commit.committer.email).isEqualTo(user.email);
+    assertThat(commit.author.email).isEqualTo(user.email());
+    assertThat(commit.committer.email).isEqualTo(user.email());
 
     // check that the author/committer was added as reviewer
     Collection<AccountInfo> reviewers = change.reviewers.get(REVIEWER);
     assertThat(reviewers).isNotNull();
     assertThat(reviewers).hasSize(1);
-    assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.getId().get());
+    assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.id().get());
     assertThat(change.reviewers.get(CC)).isNull();
 
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
     assertThat(m.from().getName()).isEqualTo("Administrator (Code Review)");
-    assertThat(m.rcpt()).containsExactly(user.emailAddress);
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
     assertThat(m.body()).contains("I'd like you to do a code review");
     assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
-    assertMailReplyTo(m, admin.email);
+    assertMailReplyTo(m, admin.email());
   }
 
   @Test
@@ -1455,18 +1462,18 @@
 
     // admin pushes commit of user
     TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
-    PushOneCommit push = pushFactory.create(user.getIdent(), repo);
+    PushOneCommit push = pushFactory.create(user.newIdent(), repo);
     PushOneCommit.Result result = push.to("refs/for/master");
     result.assertOkStatus();
 
     ChangeInfo change = gApi.changes().id(result.getChangeId()).get();
-    assertThat(change.owner._accountId).isEqualTo(admin.id.get());
+    assertThat(change.owner._accountId).isEqualTo(admin.id().get());
     CommitInfo commit = change.revisions.get(change.currentRevision).commit;
-    assertThat(commit.author.email).isEqualTo(user.email);
-    assertThat(commit.committer.email).isEqualTo(user.email);
+    assertThat(commit.author.email).isEqualTo(user.email());
+    assertThat(commit.committer.email).isEqualTo(user.email());
 
     // check the user cannot see the change
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     try {
       gApi.changes().id(result.getChangeId()).get();
       fail("Expected ResourceNotFoundException");
@@ -1486,13 +1493,13 @@
     // admin pushes commit that references 'user' in a footer
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT
                 + "\n\n"
                 + FooterConstants.REVIEWED_BY.getName()
                 + ": "
-                + user.getIdent().toExternalString(),
+                + user.newIdent().toExternalString(),
             PushOneCommit.FILE_NAME,
             PushOneCommit.FILE_CONTENT);
     PushOneCommit.Result result = push.to("refs/for/master");
@@ -1503,17 +1510,17 @@
     Collection<AccountInfo> reviewers = change.reviewers.get(REVIEWER);
     assertThat(reviewers).isNotNull();
     assertThat(reviewers).hasSize(1);
-    assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.getId().get());
+    assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.id().get());
     assertThat(change.reviewers.get(CC)).isNull();
 
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress);
-    assertThat(m.body()).contains("Hello " + user.fullName + ",\n");
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+    assertThat(m.body()).contains("Hello " + user.fullName() + ",\n");
     assertThat(m.body()).contains("I'd like you to do a code review.");
     assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
-    assertMailReplyTo(m, admin.email);
+    assertMailReplyTo(m, admin.email());
   }
 
   @Test
@@ -1530,20 +1537,20 @@
     TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             repo,
             PushOneCommit.SUBJECT
                 + "\n\n"
                 + FooterConstants.REVIEWED_BY.getName()
                 + ": "
-                + user.getIdent().toExternalString(),
+                + user.newIdent().toExternalString(),
             PushOneCommit.FILE_NAME,
             PushOneCommit.FILE_CONTENT);
     PushOneCommit.Result result = push.to("refs/for/master");
     result.assertOkStatus();
 
     // check that 'user' cannot see the change
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     try {
       gApi.changes().id(result.getChangeId()).get();
       fail("Expected ResourceNotFoundException");
@@ -1552,7 +1559,7 @@
     }
 
     // check that 'user' was NOT added as cc ('user' can't see the change)
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     ChangeInfo change = gApi.changes().id(result.getChangeId()).get();
     assertThat(change.reviewers.get(REVIEWER)).isNull();
     assertThat(change.reviewers.get(CC)).isNull();
@@ -1571,12 +1578,12 @@
 
     // create change
     TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
-    PushOneCommit push = pushFactory.create(admin.getIdent(), repo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), repo);
     PushOneCommit.Result result = push.to("refs/for/master");
     result.assertOkStatus();
 
     // check the user cannot see the change
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     try {
       gApi.changes().id(result.getChangeId()).get();
       fail("Expected ResourceNotFoundException");
@@ -1585,12 +1592,12 @@
     }
 
     // try to add user as reviewer
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     AddReviewerResult r = gApi.changes().id(result.getChangeId()).addReviewer(in);
 
-    assertThat(r.input).isEqualTo(user.email);
+    assertThat(r.input).isEqualTo(user.email());
     assertThat(r.error).contains("does not have permission to see this change");
     assertThat(r.reviewers).isNull();
   }
@@ -1673,17 +1680,17 @@
     Timestamp oldTs = rsrc.getChange().getLastUpdatedOn();
 
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     gApi.changes().id(r.getChangeId()).addReviewer(in);
 
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress);
-    assertThat(m.body()).contains("Hello " + user.fullName + ",\n");
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+    assertThat(m.body()).contains("Hello " + user.fullName() + ",\n");
     assertThat(m.body()).contains("I'd like you to do a code review.");
     assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
-    assertMailReplyTo(m, admin.email);
+    assertMailReplyTo(m, admin.email());
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
 
     // When NoteDb is enabled adding a reviewer records that user as reviewer
@@ -1693,7 +1700,7 @@
     Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
     assertThat(reviewers).isNotNull();
     assertThat(reviewers).hasSize(1);
-    assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.getId().get());
+    assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.id().get());
 
     // Ensure ETag and lastUpdatedOn are updated.
     rsrc = parseResource(r);
@@ -1702,7 +1709,7 @@
 
     // Change status of reviewer and ensure ETag is updated.
     oldETag = rsrc.getETag();
-    accountOperations.account(user.id).forUpdate().status("new status").update();
+    accountOperations.account(user.id()).forUpdate().status("new status").update();
     rsrc = parseResource(r);
     assertThat(rsrc.getETag()).isNotEqualTo(oldETag);
   }
@@ -1711,7 +1718,7 @@
   public void listReviewers() throws Exception {
     PushOneCommit.Result r = createChange();
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     gApi.changes().id(r.getChangeId()).addReviewer(in);
     assertThat(gApi.changes().id(r.getChangeId()).reviewers()).hasSize(1);
 
@@ -1727,13 +1734,13 @@
     in.state = ReviewerState.CC;
     gApi.changes().id(r.getChangeId()).addReviewer(in);
     assertThat(gApi.changes().id(r.getChangeId()).reviewers().stream().map(a -> a.username))
-        .containsExactly(user.username, username1);
+        .containsExactly(user.username(), username1);
   }
 
   @Test
   public void notificationsForAddedWorkInProgressReviewers() throws Exception {
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     ReviewInput batchIn = new ReviewInput();
     batchIn.reviewers = ImmutableList.of(in);
 
@@ -1785,7 +1792,7 @@
     String testGroup = groupOperations.newGroup().name("ab").create().get();
     GroupApi groupApi = gApi.groups().id(testGroup);
     groupApi.description("test group");
-    groupApi.addMembers(user.fullName);
+    groupApi.addMembers(user.fullName());
 
     AddReviewerInput in = new AddReviewerInput();
     in.reviewer = "abc";
@@ -1889,8 +1896,8 @@
     Timestamp oldTs = rsrc.getChange().getLastUpdatedOn();
 
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
-    requestScopeOperations.setApiUser(user.getId());
+    in.reviewer = user.email();
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(r.getChangeId()).addReviewer(in);
 
     // There should be no email notification when adding self
@@ -1904,7 +1911,7 @@
     Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
     assertThat(reviewers).isNotNull();
     assertThat(reviewers).hasSize(1);
-    assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.getId().get());
+    assertThat(reviewers.iterator().next()._accountId).isEqualTo(user.id().get());
 
     // Ensure ETag and lastUpdatedOn are updated.
     rsrc = parseResource(r);
@@ -1920,15 +1927,15 @@
   @Test
   public void implicitlyCcOnNonVotingReviewForUserWithoutUserNamePgStyle() throws Exception {
     com.google.gerrit.acceptance.TestAccount accountWithoutUsername = accountCreator.create();
-    assertThat(accountWithoutUsername.username).isNull();
+    assertThat(accountWithoutUsername.username()).isNull();
     testImplicitlyCcOnNonVotingReviewPgStyle(accountWithoutUsername);
   }
 
   private void testImplicitlyCcOnNonVotingReviewPgStyle(
       com.google.gerrit.acceptance.TestAccount testAccount) throws Exception {
     PushOneCommit.Result r = createChange();
-    requestScopeOperations.setApiUser(testAccount.getId());
-    assertThat(getReviewerState(r.getChangeId(), testAccount.id)).isEmpty();
+    requestScopeOperations.setApiUser(testAccount.id());
+    assertThat(getReviewerState(r.getChangeId(), testAccount.id())).isEmpty();
 
     // Exact request format made by PG UI at ddc6b7160fe416fed9e7e3180489d44c82fd64f8.
     ReviewInput in = new ReviewInput();
@@ -1938,13 +1945,13 @@
     in.reviewers = ImmutableList.of();
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(in);
 
-    assertThat(getReviewerState(r.getChangeId(), testAccount.id)).hasValue(CC);
+    assertThat(getReviewerState(r.getChangeId(), testAccount.id())).hasValue(CC);
   }
 
   @Test
   public void implicitlyAddReviewerOnVotingReview() throws Exception {
     PushOneCommit.Result r = createChange();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes()
         .id(r.getChangeId())
         .revision(r.getCommit().name())
@@ -1952,23 +1959,23 @@
 
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
     assertThat(c.reviewers.get(REVIEWER).stream().map(ai -> ai._accountId).collect(toList()))
-        .containsExactly(user.id.get());
+        .containsExactly(user.id().get());
 
     // Further test: remove the vote, then comment again. The user should be
     // implicitly re-added to the ReviewerSet, as a CC if we're using NoteDb.
-    requestScopeOperations.setApiUser(admin.getId());
-    gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).remove();
+    requestScopeOperations.setApiUser(admin.id());
+    gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).remove();
     c = gApi.changes().id(r.getChangeId()).get();
     assertThat(c.reviewers.values()).isEmpty();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes()
         .id(r.getChangeId())
         .revision(r.getCommit().name())
         .review(new ReviewInput().message("hi"));
     c = gApi.changes().id(r.getChangeId()).get();
     assertThat(c.reviewers.get(CC).stream().map(ai -> ai._accountId).collect(toList()))
-        .containsExactly(user.id.get());
+        .containsExactly(user.id().get());
   }
 
   @Test
@@ -1980,19 +1987,19 @@
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
     Collection<AccountInfo> reviewers = c.reviewers.get(REVIEWER);
     assertThat(reviewers).hasSize(1);
-    assertThat(reviewers.iterator().next()._accountId).isEqualTo(admin.getId().get());
+    assertThat(reviewers.iterator().next()._accountId).isEqualTo(admin.id().get());
     assertThat(c.reviewers).doesNotContainKey(CC);
 
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     gApi.changes().id(r.getChangeId()).addReviewer(in);
 
     c = gApi.changes().id(r.getChangeId()).get();
     reviewers = c.reviewers.get(REVIEWER);
     assertThat(reviewers).hasSize(2);
     Iterator<AccountInfo> reviewerIt = reviewers.iterator();
-    assertThat(reviewerIt.next()._accountId).isEqualTo(admin.getId().get());
-    assertThat(reviewerIt.next()._accountId).isEqualTo(user.getId().get());
+    assertThat(reviewerIt.next()._accountId).isEqualTo(admin.id().get());
+    assertThat(reviewerIt.next()._accountId).isEqualTo(user.id().get());
     assertThat(c.reviewers).doesNotContainKey(CC);
   }
 
@@ -2002,7 +2009,7 @@
     ChangeResource rsrc = parseResource(r);
     String oldETag = rsrc.getETag();
 
-    accountOperations.account(admin.id).forUpdate().status("new status").update();
+    accountOperations.account(admin.id()).forUpdate().status("new status").update();
     rsrc = parseResource(r);
     assertThat(rsrc.getETag()).isNotEqualTo(oldETag);
   }
@@ -2012,7 +2019,7 @@
     String changeId = createChange().getChangeId();
 
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     gApi.changes().id(changeId).addReviewer(in);
     sender.clear();
 
@@ -2027,7 +2034,7 @@
 
     assertThat(sender.getMessages()).hasSize(1);
     Message m = sender.getMessages().get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress);
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
   }
 
   @Test
@@ -2058,15 +2065,15 @@
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
 
     Map<String, Short> m =
-        gApi.changes().id(r.getChangeId()).reviewer(admin.getId().toString()).votes();
+        gApi.changes().id(r.getChangeId()).reviewer(admin.id().toString()).votes();
 
     assertThat(m).hasSize(1);
     assertThat(m).containsEntry("Code-Review", Short.valueOf((short) 2));
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.dislike());
 
-    m = gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).votes();
+    m = gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).votes();
 
     assertThat(m).hasSize(1);
     assertThat(m).containsEntry("Code-Review", Short.valueOf((short) -1));
@@ -2092,32 +2099,32 @@
 
     PushOneCommit.Result r = createChange();
     String changeId = r.getChangeId();
-    gApi.changes().id(changeId).addReviewer(user.getId().toString());
+    gApi.changes().id(changeId).addReviewer(user.id().toString());
 
     ChangeInfo c = gApi.changes().id(changeId).get(ListChangesOption.DETAILED_LABELS);
     assertThat(getReviewers(c.reviewers.get(CC))).isEmpty();
-    assertThat(getReviewers(c.reviewers.get(REVIEWER))).containsExactly(user.getId());
+    assertThat(getReviewers(c.reviewers.get(REVIEWER))).containsExactly(user.id());
 
     sender.clear();
-    gApi.changes().id(changeId).reviewer(user.getId().toString()).remove();
+    gApi.changes().id(changeId).reviewer(user.id().toString()).remove();
     assertThat(gApi.changes().id(changeId).get().reviewers).isEmpty();
 
     assertThat(sender.getMessages()).hasSize(1);
     Message message = sender.getMessages().get(0);
-    assertThat(message.body()).contains("Removed reviewer " + user.fullName + ".");
+    assertThat(message.body()).contains("Removed reviewer " + user.fullName() + ".");
     assertThat(message.body()).doesNotContain("with the following votes");
 
     // Make sure the reviewer can still be added again.
-    gApi.changes().id(changeId).addReviewer(user.getId().toString());
+    gApi.changes().id(changeId).addReviewer(user.id().toString());
     c = gApi.changes().id(changeId).get();
     assertThat(getReviewers(c.reviewers.get(CC))).isEmpty();
-    assertThat(getReviewers(c.reviewers.get(REVIEWER))).containsExactly(user.getId());
+    assertThat(getReviewers(c.reviewers.get(REVIEWER))).containsExactly(user.id());
 
     // Remove again, and then try to remove once more to verify 404 is
     // returned.
-    gApi.changes().id(changeId).reviewer(user.getId().toString()).remove();
+    gApi.changes().id(changeId).reviewer(user.id().toString()).remove();
     exception.expect(ResourceNotFoundException.class);
-    gApi.changes().id(changeId).reviewer(user.getId().toString()).remove();
+    gApi.changes().id(changeId).reviewer(user.id().toString()).remove();
   }
 
   @Test
@@ -2135,30 +2142,30 @@
     String changeId = r.getChangeId();
     gApi.changes().id(changeId).revision(r.getCommit().name()).review(ReviewInput.approve());
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(changeId).revision(r.getCommit().name()).review(ReviewInput.recommend());
 
     Collection<AccountInfo> reviewers = gApi.changes().id(changeId).get().reviewers.get(REVIEWER);
 
     assertThat(reviewers).hasSize(2);
     Iterator<AccountInfo> reviewerIt = reviewers.iterator();
-    assertThat(reviewerIt.next()._accountId).isEqualTo(admin.getId().get());
-    assertThat(reviewerIt.next()._accountId).isEqualTo(user.getId().get());
+    assertThat(reviewerIt.next()._accountId).isEqualTo(admin.id().get());
+    assertThat(reviewerIt.next()._accountId).isEqualTo(user.id().get());
 
     sender.clear();
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     DeleteReviewerInput input = new DeleteReviewerInput();
     if (!notify) {
       input.notify = NotifyHandling.NONE;
     }
-    gApi.changes().id(changeId).reviewer(user.getId().toString()).remove(input);
+    gApi.changes().id(changeId).reviewer(user.id().toString()).remove(input);
 
     if (notify) {
       assertThat(sender.getMessages()).hasSize(1);
       Message message = sender.getMessages().get(0);
       assertThat(message.body())
-          .contains("Removed reviewer " + user.fullName + " with the following votes");
-      assertThat(message.body()).contains("* Code-Review+1 by " + user.fullName);
+          .contains("Removed reviewer " + user.fullName() + " with the following votes");
+      assertThat(message.body()).contains("* Code-Review+1 by " + user.fullName());
     } else {
       assertThat(sender.getMessages()).isEmpty();
     }
@@ -2166,9 +2173,9 @@
     reviewers = gApi.changes().id(changeId).get().reviewers.get(REVIEWER);
     assertThat(reviewers).hasSize(1);
     reviewerIt = reviewers.iterator();
-    assertThat(reviewerIt.next()._accountId).isEqualTo(admin.getId().get());
+    assertThat(reviewerIt.next()._accountId).isEqualTo(admin.id().get());
 
-    eventRecorder.assertReviewerDeletedEvents(changeId, user.email);
+    eventRecorder.assertReviewerDeletedEvents(changeId, user.email());
   }
 
   @Test
@@ -2177,10 +2184,10 @@
     String changeId = r.getChangeId();
     gApi.changes().id(changeId).revision(r.getCommit().name()).review(ReviewInput.approve());
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("remove reviewer not permitted");
-    gApi.changes().id(r.getChangeId()).reviewer(admin.getId().toString()).remove();
+    gApi.changes().id(r.getChangeId()).reviewer(admin.id().toString()).remove();
   }
 
   @Test
@@ -2188,14 +2195,14 @@
     PushOneCommit.Result r = createChange();
     String changeId = r.getChangeId();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     recommend(changeId);
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     approve(changeId);
     gApi.changes().id(changeId).revision(r.getCommit().name()).submit();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("remove reviewer not permitted");
     gApi.changes().id(r.getChangeId()).reviewer("self").remove();
@@ -2206,15 +2213,15 @@
     PushOneCommit.Result r = createChange();
     String changeId = r.getChangeId();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     recommend(changeId);
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(changeId).abandon();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(r.getChangeId()).reviewer("self").remove();
-    eventRecorder.assertReviewerDeletedEvents(changeId, user.email);
+    eventRecorder.assertReviewerDeletedEvents(changeId, user.email());
   }
 
   @Test
@@ -2222,17 +2229,17 @@
     PushOneCommit.Result r = createChange();
     String changeId = r.getChangeId();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     recommend(changeId);
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     approve(changeId);
     gApi.changes().id(changeId).abandon();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("remove reviewer not permitted");
-    gApi.changes().id(r.getChangeId()).reviewer(admin.getId().toString()).remove();
+    gApi.changes().id(r.getChangeId()).reviewer(admin.id().toString()).remove();
   }
 
   @Test
@@ -2240,23 +2247,23 @@
     PushOneCommit.Result r = createChange();
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     recommend(r.getChangeId());
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     sender.clear();
-    gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote("Code-Review");
+    gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).deleteVote("Code-Review");
 
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message msg = messages.get(0);
-    assertThat(msg.rcpt()).containsExactly(user.emailAddress);
-    assertThat(msg.body()).contains(admin.fullName + " has removed a vote on this change.\n");
+    assertThat(msg.rcpt()).containsExactly(user.getEmailAddress());
+    assertThat(msg.body()).contains(admin.fullName() + " has removed a vote on this change.\n");
     assertThat(msg.body())
-        .contains("Removed Code-Review+1 by " + user.fullName + " <" + user.email + ">\n");
+        .contains("Removed Code-Review+1 by " + user.fullName() + " <" + user.email() + ">\n");
 
     Map<String, Short> m =
-        gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).votes();
+        gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).votes();
 
     // Dummy 0 approval on the change to block vote copying to this patch set.
     assertThat(m).containsExactly("Code-Review", Short.valueOf((short) 0));
@@ -2264,10 +2271,10 @@
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
 
     ChangeMessageInfo message = Iterables.getLast(c.messages);
-    assertThat(message.author._accountId).isEqualTo(admin.getId().get());
+    assertThat(message.author._accountId).isEqualTo(admin.id().get());
     assertThat(message.message).isEqualTo("Removed Code-Review+1 by User <user@example.com>\n");
     assertThat(getReviewers(c.reviewers.get(REVIEWER)))
-        .containsExactlyElementsIn(ImmutableSet.of(admin.getId(), user.getId()));
+        .containsExactlyElementsIn(ImmutableSet.of(admin.id(), user.id()));
   }
 
   @Test
@@ -2275,15 +2282,15 @@
     PushOneCommit.Result r = createChange();
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     recommend(r.getChangeId());
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     sender.clear();
     DeleteVoteInput in = new DeleteVoteInput();
     in.label = "Code-Review";
     in.notify = NotifyHandling.NONE;
-    gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote(in);
+    gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).deleteVote(in);
     assertThat(sender.getMessages()).isEmpty();
   }
 
@@ -2304,33 +2311,33 @@
         .preferredEmail(email)
         .fullname("User2")
         .create();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     recommend(r.getChangeId());
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     sender.clear();
     in.notifyDetails = new HashMap<>();
     in.notifyDetails.put(RecipientType.TO, new NotifyInfo(ImmutableList.of(email)));
-    gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote(in);
+    gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).deleteVote(in);
     assertNotifyTo(email, "User2");
 
     // notify unrelated account as CC
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     recommend(r.getChangeId());
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     sender.clear();
     in.notifyDetails = new HashMap<>();
     in.notifyDetails.put(RecipientType.CC, new NotifyInfo(ImmutableList.of(email)));
-    gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote(in);
+    gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).deleteVote(in);
     assertNotifyCc(email, "User2");
 
     // notify unrelated account as BCC
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     recommend(r.getChangeId());
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     sender.clear();
     in.notifyDetails = new HashMap<>();
     in.notifyDetails.put(RecipientType.BCC, new NotifyInfo(ImmutableList.of(email)));
-    gApi.changes().id(r.getChangeId()).reviewer(user.getId().toString()).deleteVote(in);
+    gApi.changes().id(r.getChangeId()).reviewer(user.id().toString()).deleteVote(in);
     assertNotifyBcc(email, "User2");
   }
 
@@ -2339,10 +2346,10 @@
     PushOneCommit.Result r = createChange();
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("delete vote not permitted");
-    gApi.changes().id(r.getChangeId()).reviewer(admin.getId().toString()).deleteVote("Code-Review");
+    gApi.changes().id(r.getChangeId()).reviewer(admin.id().toString()).deleteVote("Code-Review");
   }
 
   @Test
@@ -2370,31 +2377,31 @@
     // Reviewers should only be "admin"
     ChangeInfo c = gApi.changes().id(changeId).get();
     assertThat(getReviewers(c.reviewers.get(REVIEWER)))
-        .containsExactlyElementsIn(ImmutableSet.of(admin.getId()));
+        .containsExactlyElementsIn(ImmutableSet.of(admin.id()));
     assertThat(c.reviewers.get(CC)).isNull();
 
     // Add the user as reviewer
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     gApi.changes().id(changeId).addReviewer(in);
     c = gApi.changes().id(changeId).get();
     assertThat(getReviewers(c.reviewers.get(REVIEWER)))
-        .containsExactlyElementsIn(ImmutableSet.of(admin.getId(), user.getId()));
+        .containsExactlyElementsIn(ImmutableSet.of(admin.id(), user.id()));
 
     // Approve the change as user, then remove the approval
     // (only to confirm that the user does have Code-Review+2 permission)
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(changeId).revision(commit).review(ReviewInput.approve());
     gApi.changes().id(changeId).revision(commit).review(ReviewInput.noScore());
 
     // Submit the change
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(changeId).revision(commit).submit();
 
     // User should still be on the change
     c = gApi.changes().id(changeId).get();
     assertThat(getReviewers(c.reviewers.get(REVIEWER)))
-        .containsExactlyElementsIn(ImmutableSet.of(admin.getId(), user.getId()));
+        .containsExactlyElementsIn(ImmutableSet.of(admin.id(), user.id()));
   }
 
   @Test
@@ -2507,7 +2514,7 @@
     RevisionInfo rev = Iterables.getOnlyElement(result.revisions.values());
     assertThat(rev._number).isEqualTo(r.getPatchSetId().get());
     assertThat(rev.created).isNotNull();
-    assertThat(rev.uploader._accountId).isEqualTo(admin.getId().get());
+    assertThat(rev.uploader._accountId).isEqualTo(admin.id().get());
     assertThat(rev.ref).isEqualTo(r.getPatchSetId().toRefName());
     assertThat(rev.actions).isNotEmpty();
   }
@@ -2518,18 +2525,59 @@
     assertThat(
             Iterables.getOnlyElement(query("project:{" + project.get() + "} owner:self")).changeId)
         .isEqualTo(r.getChangeId());
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertThat(query("owner:self project:{" + project.get() + "}")).isEmpty();
   }
 
+  private static class OperatorModule extends AbstractModule {
+    @Override
+    public void configure() {
+      bind(ChangeOperatorFactory.class)
+          .annotatedWith(Exports.named("mytopic"))
+          .toInstance((cqb, value) -> new MyTopicPredicate(value));
+    }
+
+    private static class MyTopicPredicate extends PostFilterPredicate<ChangeData> {
+      MyTopicPredicate(String value) {
+        super("mytopic", value);
+      }
+
+      @Override
+      public boolean match(ChangeData cd) {
+        return Objects.equals(cd.change().getTopic(), value);
+      }
+
+      @Override
+      public int getCost() {
+        return 2;
+      }
+    }
+  }
+
+  @Test
+  public void queryChangesPluginOperator() throws Exception {
+    PushOneCommit.Result r = createChange();
+    String query = "mytopic_myplugin:foo";
+    String expectedMessage = "Unsupported operator mytopic_myplugin:foo";
+    assertThatQueryException(query).hasMessageThat().isEqualTo(expectedMessage);
+
+    try (AutoCloseable ignored = installPlugin("myplugin", OperatorModule.class)) {
+      assertThat(query(query)).isEmpty();
+      gApi.changes().id(r.getChangeId()).topic("foo");
+      assertThat(query(query).stream().map(i -> i.changeId)).containsExactly(r.getChangeId());
+    }
+
+    assertThatQueryException(query).hasMessageThat().isEqualTo(expectedMessage);
+  }
+
   @Test
   public void checkReviewedFlagBeforeAndAfterReview() throws Exception {
     PushOneCommit.Result r = createChange();
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     gApi.changes().id(r.getChangeId()).addReviewer(in);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertThat(get(r.getChangeId(), REVIEWED).reviewed).isNull();
 
     revision(r).review(ReviewInput.recommend());
@@ -2550,7 +2598,7 @@
   public void editTopicWithoutPermissionNotAllowed() throws Exception {
     PushOneCommit.Result r = createChange();
     assertThat(gApi.changes().id(r.getChangeId()).topic()).isEqualTo("");
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("edit topic name not permitted");
     gApi.changes().id(r.getChangeId()).topic("mytopic");
@@ -2561,7 +2609,7 @@
     PushOneCommit.Result r = createChange();
     assertThat(gApi.changes().id(r.getChangeId()).topic()).isEqualTo("");
     grant(project, "refs/heads/master", Permission.EDIT_TOPIC_NAME, false, REGISTERED_USERS);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(r.getChangeId()).topic("mytopic");
     assertThat(gApi.changes().id(r.getChangeId()).topic()).isEqualTo("mytopic");
   }
@@ -2605,7 +2653,7 @@
   public void submitNotAllowedWithoutPermission() throws Exception {
     PushOneCommit.Result r = createChange();
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("submit not permitted");
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
@@ -2616,7 +2664,7 @@
     PushOneCommit.Result r = createChange();
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
     grant(project, "refs/heads/master", Permission.SUBMIT, false, REGISTERED_USERS);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
     assertThat(gApi.changes().id(r.getChangeId()).info().status).isEqualTo(ChangeStatus.MERGED);
   }
@@ -2652,7 +2700,7 @@
     r1.assertOkStatus();
     PushOneCommit.Result r2 =
         pushFactory
-            .create(admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new content", r1.getChangeId())
+            .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "new content", r1.getChangeId())
             .to("refs/for/master");
     r2.assertOkStatus();
 
@@ -2732,7 +2780,7 @@
 
   @Test
   public void defaultSearchDoesNotTouchDatabase() throws Exception {
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     PushOneCommit.Result r1 = createChange();
     gApi.changes()
         .id(r1.getChangeId())
@@ -2742,7 +2790,7 @@
 
     createChange();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     try (AutoCloseable ignored = disableNoteDb()) {
       assertThat(
               gApi.changes()
@@ -2761,12 +2809,12 @@
   public void votable() throws Exception {
     PushOneCommit.Result r = createChange();
     String triplet = project.get() + "~master~" + r.getChangeId();
-    gApi.changes().id(triplet).addReviewer(user.username);
+    gApi.changes().id(triplet).addReviewer(user.username());
     ChangeInfo c = gApi.changes().id(triplet).get(DETAILED_LABELS);
     LabelInfo codeReview = c.labels.get("Code-Review");
     assertThat(codeReview.all).hasSize(1);
     ApprovalInfo approval = codeReview.all.get(0);
-    assertThat(approval._accountId).isEqualTo(user.id.get());
+    assertThat(approval._accountId).isEqualTo(user.id().get());
     assertThat(approval.value).isEqualTo(0);
 
     try (ProjectConfigUpdate u = updateProject(project)) {
@@ -2778,7 +2826,7 @@
     codeReview = c.labels.get("Code-Review");
     assertThat(codeReview.all).hasSize(1);
     approval = codeReview.all.get(0);
-    assertThat(approval._accountId).isEqualTo(user.id.get());
+    assertThat(approval._accountId).isEqualTo(user.id().get());
     assertThat(approval.value).isNull();
   }
 
@@ -2827,7 +2875,7 @@
   public void noteDbCommitsOnPatchSetCreation() throws Exception {
     PushOneCommit.Result r = createChange();
     pushFactory
-        .create(admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "4711", r.getChangeId())
+        .create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "4711", r.getChangeId())
         .to("refs/for/master")
         .assertOkStatus();
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
@@ -2838,7 +2886,7 @@
 
       assertThat(commitPatchSetCreation.getShortMessage()).isEqualTo("Create patch set 2");
       PersonIdent expectedAuthor =
-          changeNoteUtil.newIdent(getAccount(admin.id), c.updated, serverIdent.get());
+          changeNoteUtil.newIdent(getAccount(admin.id()), c.updated, serverIdent.get());
       assertThat(commitPatchSetCreation.getAuthorIdent()).isEqualTo(expectedAuthor);
       assertThat(commitPatchSetCreation.getCommitterIdent())
           .isEqualTo(new PersonIdent(serverIdent.get(), c.updated));
@@ -2846,7 +2894,8 @@
 
       RevCommit commitChangeCreation = rw.parseCommit(commitPatchSetCreation.getParent(0));
       assertThat(commitChangeCreation.getShortMessage()).isEqualTo("Create change");
-      expectedAuthor = changeNoteUtil.newIdent(getAccount(admin.id), c.created, serverIdent.get());
+      expectedAuthor =
+          changeNoteUtil.newIdent(getAccount(admin.id()), c.created, serverIdent.get());
       assertThat(commitChangeCreation.getAuthorIdent()).isEqualTo(expectedAuthor);
       assertThat(commitChangeCreation.getCommitterIdent())
           .isEqualTo(new PersonIdent(serverIdent.get(), c.created));
@@ -2893,7 +2942,7 @@
     block(p, "refs/for/*", Permission.ADD_PATCH_SET, REGISTERED_USERS);
 
     // Create change as admin
-    PushOneCommit push = pushFactory.create(admin.getIdent(), adminTestRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), adminTestRepo);
     PushOneCommit.Result r1 = push.to("refs/for/master");
     r1.assertOkStatus();
 
@@ -2913,7 +2962,7 @@
     TestRepository<?> userTestRepo = cloneProject(project, user);
 
     // Create change as admin
-    PushOneCommit push = pushFactory.create(admin.getIdent(), adminTestRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), adminTestRepo);
     PushOneCommit.Result r1 = push.to("refs/for/master");
     r1.assertOkStatus();
 
@@ -2937,7 +2986,7 @@
     block(p, "refs/for/*", Permission.ADD_PATCH_SET, REGISTERED_USERS);
 
     // Create change as admin
-    PushOneCommit push = pushFactory.create(admin.getIdent(), adminTestRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), adminTestRepo);
     PushOneCommit.Result r1 = push.to("refs/for/master");
     r1.assertOkStatus();
 
@@ -2969,7 +3018,7 @@
     createBranch("dev");
     PushOneCommit.Result changeA =
         pushFactory
-            .create(user.getIdent(), testRepo, "change A", "A.txt", "A content")
+            .create(user.newIdent(), testRepo, "change A", "A.txt", "A content")
             .to("refs/heads/dev");
     changeA.assertOkStatus();
     MergeInput mergeInput = new MergeInput();
@@ -3005,7 +3054,7 @@
     createBranch("dev");
     PushOneCommit.Result changeA =
         pushFactory
-            .create(user.getIdent(), testRepo, "change A", "A.txt", "A content")
+            .create(user.newIdent(), testRepo, "change A", "A.txt", "A content")
             .to("refs/heads/dev");
     changeA.assertOkStatus();
     MergeInput mergeInput = new MergeInput();
@@ -3041,7 +3090,7 @@
     gApi.changes().id(baseChange).setPrivate(true, "set private");
 
     // Create the destination change on 'master' branch.
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     testRepo.reset(initialHead);
     String changeId = createChange().getChangeId();
 
@@ -3179,7 +3228,7 @@
     testRepo.reset("config");
     PushOneCommit push2 =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             "Ignore Verified",
             "rules.pl",
@@ -3225,7 +3274,7 @@
     testRepo.reset("config");
     PushOneCommit push2 =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             "Configure Non-Author-Code-Review",
             "rules.pl",
@@ -3262,10 +3311,10 @@
 
     PushOneCommit.Result r = createChange();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(ReviewInput.approve());
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).submit();
 
     ChangeInfo change = gApi.changes().id(r.getChangeId()).get();
@@ -3279,7 +3328,7 @@
   public void checkLabelsForAutoClosedChange() throws Exception {
     PushOneCommit.Result r = createChange();
 
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result result = push.to("refs/heads/master");
     result.assertOkStatus();
 
@@ -3298,13 +3347,13 @@
     PushOneCommit.Result r = createChange();
     String triplet = project.get() + "~master~" + r.getChangeId();
 
-    gApi.changes().id(triplet).addReviewer(user.username);
+    gApi.changes().id(triplet).addReviewer(user.username());
 
     ChangeInfo c = gApi.changes().id(triplet).get(DETAILED_LABELS);
     LabelInfo codeReview = c.labels.get("Code-Review");
     assertThat(codeReview.all).hasSize(1);
     ApprovalInfo approval = codeReview.all.get(0);
-    assertThat(approval._accountId).isEqualTo(user.id.get());
+    assertThat(approval._accountId).isEqualTo(user.id().get());
     assertThat(approval.permittedVotingRange).isNotNull();
     // default values
     assertThat(approval.permittedVotingRange.min).isEqualTo(-1);
@@ -3325,7 +3374,7 @@
     codeReview = c.labels.get("Code-Review");
     assertThat(codeReview.all).hasSize(1);
     approval = codeReview.all.get(0);
-    assertThat(approval._accountId).isEqualTo(user.id.get());
+    assertThat(approval._accountId).isEqualTo(user.id().get());
     assertThat(approval.permittedVotingRange).isNotNull();
     assertThat(approval.permittedVotingRange.min).isEqualTo(minPermittedValue);
     assertThat(approval.permittedVotingRange.max).isEqualTo(maxPermittedValue);
@@ -3341,13 +3390,13 @@
     PushOneCommit.Result r = createChange();
     String triplet = project.get() + "~master~" + r.getChangeId();
 
-    gApi.changes().id(triplet).addReviewer(user.username);
+    gApi.changes().id(triplet).addReviewer(user.username());
 
     ChangeInfo c = gApi.changes().id(triplet).get(DETAILED_LABELS);
     LabelInfo codeReview = c.labels.get("Code-Review");
     assertThat(codeReview.all).hasSize(1);
     ApprovalInfo approval = codeReview.all.get(0);
-    assertThat(approval._accountId).isEqualTo(user.id.get());
+    assertThat(approval._accountId).isEqualTo(user.id().get());
     assertThat(approval.permittedVotingRange).isNull();
   }
 
@@ -3359,7 +3408,8 @@
     ReviewInput input = ReviewInput.approve().label("Code-Style", 1);
     gApi.changes().id(changeId).current().review(input);
 
-    Map<String, Short> votes = gApi.changes().id(changeId).current().reviewer(admin.email).votes();
+    Map<String, Short> votes =
+        gApi.changes().id(changeId).current().reviewer(admin.email()).votes();
     assertThat(votes.keySet()).containsExactly("Code-Review");
     assertThat(votes.values()).containsExactly((short) 2);
   }
@@ -3372,7 +3422,8 @@
     ReviewInput input = new ReviewInput().label("Code-Review", 3);
     gApi.changes().id(changeId).current().review(input);
 
-    Map<String, Short> votes = gApi.changes().id(changeId).current().reviewer(admin.email).votes();
+    Map<String, Short> votes =
+        gApi.changes().id(changeId).current().reviewer(admin.email()).votes();
     assertThat(votes).isEmpty();
   }
 
@@ -3413,10 +3464,10 @@
 
     String oldHead = getRemoteHead().name();
     PushOneCommit.Result result1 =
-        pushFactory.create(user.getIdent(), testRepo).to("refs/for/master");
+        pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
     testRepo.reset(oldHead);
     PushOneCommit.Result result2 =
-        pushFactory.create(user.getIdent(), testRepo).to("refs/for/master");
+        pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
 
     addComment(result1, "comment 1", true, false, null);
     addComment(result2, "comment 2", true, true, null);
@@ -3434,7 +3485,7 @@
     addPureRevertSubmitRule();
 
     // Create a change that is not a revert of another change
-    PushOneCommit.Result r1 = pushFactory.create(user.getIdent(), testRepo).to("refs/for/master");
+    PushOneCommit.Result r1 = pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
     approve(r1.getChangeId());
 
     exception.expect(ResourceConflictException.class);
@@ -3445,7 +3496,7 @@
 
   @Test
   public void pureRevertFactBlocksSubmissionOfNonPureReverts() throws Exception {
-    PushOneCommit.Result r1 = pushFactory.create(user.getIdent(), testRepo).to("refs/for/master");
+    PushOneCommit.Result r1 = pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
     merge(r1);
 
     addPureRevertSubmitRule();
@@ -3464,7 +3515,7 @@
   @Test
   public void pureRevertFactAllowsSubmissionOfPureReverts() throws Exception {
     // Create a change that we can later revert
-    PushOneCommit.Result r1 = pushFactory.create(user.getIdent(), testRepo).to("refs/for/master");
+    PushOneCommit.Result r1 = pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
     merge(r1);
 
     addPureRevertSubmitRule();
@@ -3485,9 +3536,9 @@
         .isEqualTo("test commit\n\nChange-Id: " + r.getChangeId() + "\n");
 
     for (com.google.gerrit.acceptance.TestAccount acc : ImmutableList.of(admin, user)) {
-      requestScopeOperations.setApiUser(acc.getId());
+      requestScopeOperations.setApiUser(acc.id());
       String newMessage =
-          "modified commit by " + acc.username + "\n\nChange-Id: " + r.getChangeId() + "\n";
+          "modified commit by " + acc.username() + "\n\nChange-Id: " + r.getChangeId() + "\n";
       gApi.changes().id(r.getChangeId()).setMessage(newMessage);
       RevisionApi rApi = gApi.changes().id(r.getChangeId()).current();
       assertThat(rApi.files().keySet()).containsExactly("/COMMIT_MSG", "a.txt");
@@ -3504,7 +3555,7 @@
 
     // Move the change to WIP and edit the commit message again, to observe a
     // different tag. Must switch to change owner to move into WIP.
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(r.getChangeId()).setWorkInProgress();
     String newMessage = "modified commit in WIP change\n\nChange-Id: " + r.getChangeId() + "\n";
     gApi.changes().id(r.getChangeId()).setMessage(newMessage);
@@ -3574,7 +3625,7 @@
     // Block default permission
     block(p, "refs/for/*", Permission.ADD_PATCH_SET, REGISTERED_USERS);
     // Create change as user
-    PushOneCommit push = pushFactory.create(user.getIdent(), userTestRepo);
+    PushOneCommit push = pushFactory.create(user.newIdent(), userTestRepo);
     PushOneCommit.Result r = push.to("refs/for/master");
     r.assertOkStatus();
     // Try to change the commit message
@@ -3604,7 +3655,7 @@
     String subject = "A happy change " + smile;
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), testRepo, subject, FILE_NAME, FILE_CONTENT)
+            .create(admin.newIdent(), testRepo, subject, FILE_NAME, FILE_CONTENT)
             .to("refs/for/master");
     r.assertOkStatus();
     String id = r.getChangeId();
@@ -3762,7 +3813,7 @@
       u.save();
     }
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     PushOneCommit.Result r = createChange();
     String changeId = r.getChangeId();
 
@@ -3776,13 +3827,13 @@
     input.label(label, 1);
     gApi.changes().id(changeId).current().review(input);
 
-    assertThat(gApi.changes().id(changeId).current().reviewer(user.email).votes().keySet())
+    assertThat(gApi.changes().id(changeId).current().reviewer(user.email()).votes().keySet())
         .containsExactly(codeReviewLabel, label);
-    assertThat(gApi.changes().id(changeId).current().reviewer(user.email).votes().values())
+    assertThat(gApi.changes().id(changeId).current().reviewer(user.email()).votes().values())
         .containsExactly((short) 2, (short) 1);
     assertThat(gApi.changes().id(changeId).get().submittable).isTrue();
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     // Remove user's permission for 'Label'.
     try (ProjectConfigUpdate u = updateProject(project)) {
       Util.remove(u.getConfig(), Permission.forLabel(label), registered, "refs/heads/*");
@@ -3794,16 +3845,16 @@
     }
 
     // Verify user's new permitted range.
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     change = gApi.changes().id(changeId).get();
     assertPermitted(change, label);
     assertPermitted(change, codeReviewLabel, -1, 0, 1);
 
-    assertThat(gApi.changes().id(changeId).current().reviewer(user.email).votes().values())
+    assertThat(gApi.changes().id(changeId).current().reviewer(user.email()).votes().values())
         .containsExactly((short) 2, (short) 1);
     assertThat(gApi.changes().id(changeId).get().submittable).isTrue();
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(changeId).current().submit();
   }
 
@@ -3906,8 +3957,8 @@
       testRepo
           .branch(RefNames.REFS_CONFIG)
           .commit()
-          .author(admin.getIdent())
-          .committer(admin.getIdent())
+          .author(admin.newIdent())
+          .committer(admin.newIdent())
           .add("rules.pl", newContent)
           .message("Modify rules.pl")
           .create();
@@ -3921,7 +3972,7 @@
   public void trackingIds() throws Exception {
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT + "\n\n" + "Bug:JRA001",
             PushOneCommit.FILE_NAME,
@@ -3970,20 +4021,20 @@
     PushOneCommit.Result r = createChange();
 
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     gApi.changes().id(r.getChangeId()).addReviewer(in);
 
     in = new AddReviewerInput();
     in.reviewer = email;
     gApi.changes().id(r.getChangeId()).addReviewer(in);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(r.getChangeId()).ignore(true);
     assertThat(gApi.changes().id(r.getChangeId()).ignored()).isTrue();
 
     // New patch set notification is not sent to users ignoring the change
     sender.clear();
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     amendChange(r.getChangeId());
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
@@ -4004,7 +4055,7 @@
     assertThat(messages).hasSize(1);
     assertThat(messages.get(0).rcpt()).containsExactly(address);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(r.getChangeId()).ignore(false);
     assertThat(gApi.changes().id(r.getChangeId()).ignored()).isFalse();
   }
@@ -4022,7 +4073,7 @@
   public void cannotIgnoreStarredChange() throws Exception {
     String changeId = createChange().getChangeId();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.accounts().self().starChange(changeId);
     assertThat(gApi.changes().id(changeId).get().starred).isTrue();
 
@@ -4040,7 +4091,7 @@
   public void cannotStarIgnoredChange() throws Exception {
     String changeId = createChange().getChangeId();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(changeId).ignore(true);
     assertThat(gApi.changes().id(changeId).ignored()).isTrue();
 
@@ -4061,31 +4112,31 @@
     PushOneCommit.Result r = createChange();
 
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     gApi.changes().id(r.getChangeId()).addReviewer(in);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertThat(gApi.changes().id(r.getChangeId()).get().reviewed).isNull();
     gApi.changes().id(r.getChangeId()).markAsReviewed(true);
     assertThat(gApi.changes().id(r.getChangeId()).get().reviewed).isTrue();
 
-    requestScopeOperations.setApiUser(user2.getId());
+    requestScopeOperations.setApiUser(user2.id());
     sender.clear();
     amendChange(r.getChangeId());
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertThat(gApi.changes().id(r.getChangeId()).get().reviewed).isNull();
 
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
-    assertThat(messages.get(0).rcpt()).containsExactly(user.emailAddress);
+    assertThat(messages.get(0).rcpt()).containsExactly(user.getEmailAddress());
   }
 
   @Test
   public void cannotSetUnreviewedLabelForPatchSetThatAlreadyHasReviewedLabel() throws Exception {
     String changeId = createChange().getChangeId();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(changeId).markAsReviewed(true);
     assertThat(gApi.changes().id(changeId).get().reviewed).isTrue();
 
@@ -4110,7 +4161,7 @@
   public void cannotSetReviewedLabelForPatchSetThatAlreadyHasUnreviewedLabel() throws Exception {
     String changeId = createChange().getChangeId();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(changeId).markAsReviewed(false);
     assertThat(gApi.changes().id(changeId).get().reviewed).isNull();
 
@@ -4135,7 +4186,7 @@
   public void setReviewedAndUnreviewedLabelsForDifferentPatchSets() throws Exception {
     String changeId = createChange().getChangeId();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(changeId).markAsReviewed(true);
     assertThat(gApi.changes().id(changeId).get().reviewed).isTrue();
 
@@ -4194,4 +4245,13 @@
   private BranchApi createBranch(String branch) throws Exception {
     return createBranch(new Branch.NameKey(project, branch));
   }
+
+  private ThrowableSubject assertThatQueryException(String query) throws Exception {
+    try {
+      query(query);
+    } catch (BadRequestException e) {
+      return assertThat(e);
+    }
+    throw new AssertionError("expected BadRequestException");
+  }
 }
diff --git a/javatests/com/google/gerrit/acceptance/api/change/DisablePrivateChangesIT.java b/javatests/com/google/gerrit/acceptance/api/change/DisablePrivateChangesIT.java
index 3ae38ca..ae88afd 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/DisablePrivateChangesIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/DisablePrivateChangesIT.java
@@ -54,7 +54,7 @@
   @GerritConfig(name = "change.disablePrivateChanges", value = "true")
   public void pushPrivatesWithDisablePrivateChangesTrue() throws Exception {
     PushOneCommit.Result result =
-        pushFactory.create(admin.getIdent(), testRepo).to("refs/for/master%private");
+        pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%private");
     result.assertErrorStatus();
   }
 
@@ -64,11 +64,11 @@
   public void pushDraftsWithDisablePrivateChangesTrue() throws Exception {
     RevCommit initialHead = getRemoteHead();
     PushOneCommit.Result result =
-        pushFactory.create(admin.getIdent(), testRepo).to("refs/for/master%draft");
+        pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%draft");
     result.assertErrorStatus();
 
     testRepo.reset(initialHead);
-    result = pushFactory.create(admin.getIdent(), testRepo).to("refs/drafts/master");
+    result = pushFactory.create(admin.newIdent(), testRepo).to("refs/drafts/master");
     result.assertErrorStatus();
   }
 
@@ -76,7 +76,7 @@
   @GerritConfig(name = "change.disablePrivateChanges", value = "true")
   public void pushWithDisablePrivateChangesTrue() throws Exception {
     PushOneCommit.Result result =
-        pushFactory.create(admin.getIdent(), testRepo).to("refs/for/master");
+        pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master");
     result.assertOkStatus();
     assertThat(result.getChange().change().isPrivate()).isFalse();
   }
@@ -85,7 +85,7 @@
   @GerritConfig(name = "change.allowDrafts", value = "true")
   public void pushPrivatesWithDisablePrivateChangesFalse() throws Exception {
     PushOneCommit.Result result =
-        pushFactory.create(admin.getIdent(), testRepo).to("refs/for/master%private");
+        pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%private");
     assertThat(result.getChange().change().isPrivate()).isTrue();
   }
 
@@ -94,11 +94,11 @@
   public void pushDraftsWithDisablePrivateChangesFalse() throws Exception {
     RevCommit initialHead = getRemoteHead();
     PushOneCommit.Result result =
-        pushFactory.create(admin.getIdent(), testRepo).to("refs/for/master%draft");
+        pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%draft");
     assertThat(result.getChange().change().isPrivate()).isTrue();
 
     testRepo.reset(initialHead);
-    result = pushFactory.create(admin.getIdent(), testRepo).to("refs/drafts/master");
+    result = pushFactory.create(admin.newIdent(), testRepo).to("refs/drafts/master");
     assertThat(result.getChange().change().isPrivate()).isTrue();
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/api/change/MergeListIT.java b/javatests/com/google/gerrit/acceptance/api/change/MergeListIT.java
index 81519a7..a08d417 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/MergeListIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/MergeListIT.java
@@ -55,7 +55,7 @@
     PushOneCommit.Result gp1 =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 testRepo,
                 "grand parent 1",
                 ImmutableMap.of("foo", "foo-1.1", "bar", "bar-1.1"))
@@ -65,7 +65,7 @@
     PushOneCommit.Result p1 =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 testRepo,
                 "parent 1",
                 ImmutableMap.of("foo", "foo-1.2", "bar", "bar-1.2"))
@@ -78,7 +78,7 @@
     PushOneCommit.Result gp2 =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 testRepo,
                 "grand parent 2",
                 ImmutableMap.of("foo", "foo-2.1", "bar", "bar-2.1"))
@@ -88,7 +88,7 @@
     PushOneCommit.Result p2 =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 testRepo,
                 "parent 2",
                 ImmutableMap.of("foo", "foo-2.2", "bar", "bar-2.2"))
@@ -97,7 +97,7 @@
 
     PushOneCommit m =
         pushFactory.create(
-            admin.getIdent(), testRepo, "merge", ImmutableMap.of("foo", "foo-1", "bar", "bar-2"));
+            admin.newIdent(), testRepo, "merge", ImmutableMap.of("foo", "foo-1", "bar", "bar-2"));
     m.setParents(ImmutableList.of(p1.getCommit(), p2.getCommit()));
     PushOneCommit.Result result = m.to("refs/for/master");
     result.assertOkStatus();
diff --git a/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java b/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java
index 7973241..bfcb1a8 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/PrivateChangeIT.java
@@ -47,9 +47,9 @@
   public void setPrivateByOwner() throws Exception {
     TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
     PushOneCommit.Result result =
-        pushFactory.create(user.getIdent(), userRepo).to("refs/for/master");
+        pushFactory.create(user.newIdent(), userRepo).to("refs/for/master");
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     String changeId = result.getChangeId();
     assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
 
@@ -111,13 +111,13 @@
   public void administratorCanSetUserChangePrivate() throws Exception {
     TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
     PushOneCommit.Result result =
-        pushFactory.create(user.getIdent(), userRepo).to("refs/for/master");
+        pushFactory.create(user.newIdent(), userRepo).to("refs/for/master");
 
     String changeId = result.getChangeId();
     assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
 
     gApi.changes().id(changeId).setPrivate(true, null);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     ChangeInfo info = gApi.changes().id(changeId).get();
     assertThat(info.isPrivate).isTrue();
   }
@@ -125,7 +125,7 @@
   @Test
   public void cannotSetOtherUsersChangePrivate() throws Exception {
     PushOneCommit.Result result = createChange();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("not allowed to mark private");
     gApi.changes().id(result.getChangeId()).setPrivate(true, null);
@@ -135,22 +135,22 @@
   public void accessPrivate() throws Exception {
     TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
     PushOneCommit.Result result =
-        pushFactory.create(user.getIdent(), userRepo).to("refs/for/master");
+        pushFactory.create(user.newIdent(), userRepo).to("refs/for/master");
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(result.getChangeId()).setPrivate(true, null);
     // Owner can always access its private changes.
     assertThat(gApi.changes().id(result.getChangeId()).get().isPrivate).isTrue();
 
     // Add admin as a reviewer.
-    gApi.changes().id(result.getChangeId()).addReviewer(admin.getId().toString());
+    gApi.changes().id(result.getChangeId()).addReviewer(admin.id().toString());
 
     // This change should be visible for admin as a reviewer.
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     assertThat(gApi.changes().id(result.getChangeId()).get().isPrivate).isTrue();
 
     // Remove admin from reviewers.
-    gApi.changes().id(result.getChangeId()).reviewer(admin.getId().toString()).remove();
+    gApi.changes().id(result.getChangeId()).reviewer(admin.id().toString()).remove();
 
     // This change should not be visible for admin anymore.
     exception.expect(ResourceNotFoundException.class);
@@ -164,7 +164,7 @@
     gApi.changes().id(result.getChangeId()).setPrivate(true, null);
 
     allow("refs/*", Permission.VIEW_PRIVATE_CHANGES, REGISTERED_USERS);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertThat(gApi.changes().id(result.getChangeId()).get().isPrivate).isTrue();
   }
 
@@ -183,14 +183,14 @@
   public void ownerCannotMarkPrivateAfterMerging() throws Exception {
     TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
     PushOneCommit.Result result =
-        pushFactory.create(user.getIdent(), userRepo).to("refs/for/master");
+        pushFactory.create(user.newIdent(), userRepo).to("refs/for/master");
 
     String changeId = result.getChangeId();
     assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
 
     merge(result);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("not allowed to mark private");
     gApi.changes().id(changeId).setPrivate(true, null);
@@ -213,14 +213,14 @@
   public void ownerCanUnmarkPrivateAfterMerging() throws Exception {
     TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
     PushOneCommit.Result result =
-        pushFactory.create(user.getIdent(), userRepo).to("refs/for/master");
+        pushFactory.create(user.newIdent(), userRepo).to("refs/for/master");
 
     String changeId = result.getChangeId();
-    gApi.changes().id(changeId).addReviewer(admin.getId().toString());
+    gApi.changes().id(changeId).addReviewer(admin.id().toString());
     merge(result);
     markMergedChangePrivate(new Change.Id(gApi.changes().id(changeId).get()._number));
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(changeId).setPrivate(false, null);
     assertThat(gApi.changes().id(changeId).get().isPrivate).isNull();
   }
@@ -230,7 +230,7 @@
     PushOneCommit.Result r = createChange();
     gApi.changes().id(r.getChangeId()).setPrivate(true);
 
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result result = push.to("refs/heads/master");
     result.assertOkStatus();
 
@@ -240,7 +240,7 @@
   private void markMergedChangePrivate(Change.Id changeId) throws Exception {
     try (BatchUpdate u =
         batchUpdateFactory.create(
-            project, identifiedUserFactory.create(admin.id), TimeUtil.nowTs())) {
+            project, identifiedUserFactory.create(admin.id()), TimeUtil.nowTs())) {
       u.addOp(
               changeId,
               new BatchUpdateOp() {
diff --git a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
index 2a295b2..bc4dca9 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
@@ -422,8 +422,8 @@
         testRepo.amendRef("HEAD").insertChangeId(changeId.substring(1));
     commitBuilder
         .message("New subject " + System.nanoTime())
-        .author(admin.getIdent())
-        .committer(new PersonIdent(admin.getIdent(), testRepo.getDate()));
+        .author(admin.newIdent())
+        .committer(new PersonIdent(admin.newIdent(), testRepo.getDate()));
     commitBuilder.create();
     GitUtil.pushHead(testRepo, "refs/for/master", false);
     assertThat(getChangeKind(changeId)).isEqualTo(NO_CODE_CHANGE);
@@ -437,8 +437,8 @@
         testRepo.amendRef("HEAD").insertChangeId(changeId.substring(1));
     commitBuilder
         .message(commitMessage)
-        .author(admin.getIdent())
-        .committer(new PersonIdent(admin.getIdent(), testRepo.getDate()));
+        .author(admin.newIdent())
+        .committer(new PersonIdent(admin.newIdent(), testRepo.getDate()));
     commitBuilder.create();
     GitUtil.pushHead(testRepo, "refs/for/master", false);
     assertThat(getChangeKind(changeId)).isEqualTo(NO_CHANGE);
@@ -447,7 +447,7 @@
   private void rework(String changeId) throws Exception {
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             PushOneCommit.FILE_NAME,
@@ -458,11 +458,11 @@
   }
 
   private void trivialRebase(String changeId) throws Exception {
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     testRepo.reset(getRemoteHead());
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             "Other Change",
             "a" + System.nanoTime() + ".txt",
@@ -488,7 +488,7 @@
 
     testRepo.reset(parent1.getCommit());
 
-    PushOneCommit merge = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit merge = pushFactory.create(admin.newIdent(), testRepo);
     merge.setParents(ImmutableList.of(parent1.getCommit(), parent2.getCommit()));
     PushOneCommit.Result result = merge.to("refs/for/master");
     result.assertOkStatus();
@@ -505,7 +505,7 @@
     testRepo.reset(parent1);
     PushOneCommit.Result newParent1 = createChange("new parent 1", "p1-1.txt", "content 1-1");
 
-    PushOneCommit merge = pushFactory.create(admin.getIdent(), testRepo, changeId);
+    PushOneCommit merge = pushFactory.create(admin.newIdent(), testRepo, changeId);
     merge.setParents(ImmutableList.of(newParent1.getCommit(), commitParent2));
     PushOneCommit.Result result = merge.to("refs/for/master");
     result.assertOkStatus();
@@ -529,7 +529,7 @@
     PushOneCommit.Result r =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 testRepo,
                 PushOneCommit.SUBJECT,
                 "other.txt",
@@ -556,21 +556,21 @@
   }
 
   private void vote(TestAccount user, String changeId, String label, int vote) throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(changeId).current().review(new ReviewInput().label(label, vote));
   }
 
   private void vote(TestAccount user, String changeId, int codeReviewVote, int verifiedVote)
       throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     ReviewInput in =
         new ReviewInput().label("Code-Review", codeReviewVote).label("Verified", verifiedVote);
     gApi.changes().id(changeId).current().review(in);
   }
 
   private void deleteVote(TestAccount user, String changeId, String label) throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
-    gApi.changes().id(changeId).reviewer(user.getId().toString()).deleteVote(label);
+    requestScopeOperations.setApiUser(user.id());
+    gApi.changes().id(changeId).reviewer(user.id().toString()).deleteVote(label);
   }
 
   private void assertVotes(ChangeInfo c, TestAccount user, int codeReviewVote, int verifiedVote) {
@@ -588,7 +588,7 @@
     Integer vote = 0;
     if (c.labels.get(label) != null && c.labels.get(label).all != null) {
       for (ApprovalInfo approval : c.labels.get(label).all) {
-        if (approval._accountId == user.id.get()) {
+        if (approval._accountId == user.id().get()) {
           vote = approval.value;
           break;
         }
diff --git a/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java b/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
index 9517bea..6c0e9ab 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/SubmitTypeRuleIT.java
@@ -137,7 +137,7 @@
   private PushOneCommit.Result createChange(String dest, String subject) throws Exception {
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             subject,
             "file" + fileCounter.incrementAndGet(),
diff --git a/javatests/com/google/gerrit/acceptance/api/group/BUILD b/javatests/com/google/gerrit/acceptance/api/group/BUILD
index da36a02..a12342a 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/BUILD
+++ b/javatests/com/google/gerrit/acceptance/api/group/BUILD
@@ -21,7 +21,6 @@
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/reviewdb:server",
         "//java/com/google/gerrit/server",
-        "//lib:gwtorm",
         "//lib/truth",
     ],
 )
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
index 3f09db9..d7d311b 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
@@ -20,7 +20,7 @@
 import static com.google.gerrit.truth.OptionalSubject.assertThat;
 
 import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
 import com.google.gerrit.extensions.api.GerritApi;
 import com.google.gerrit.extensions.common.GroupInfo;
 import com.google.gerrit.extensions.restapi.RestApiException;
@@ -36,7 +36,6 @@
 import com.google.gerrit.testing.InMemoryTestEnvironment;
 import com.google.gerrit.truth.ListSubject;
 import com.google.gerrit.truth.OptionalSubject;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -158,7 +157,7 @@
 
   private void updateGroupWithoutCacheOrIndex(
       AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
-      throws OrmException, NoSuchGroupException, IOException, ConfigInvalidException {
+      throws NoSuchGroupException, IOException, ConfigInvalidException {
     groupsUpdate.updateGroupInNoteDb(groupUuid, groupUpdate);
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
index 491cb3a..e269e68 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
@@ -64,8 +64,8 @@
     String name1 = groupOperations.newGroup().name("g1").create().get();
     String name2 = groupOperations.newGroup().name("g2").create().get();
 
-    gApi.groups().id(name1).addMembers(user.fullName);
-    gApi.groups().id(name2).addMembers(admin.fullName);
+    gApi.groups().id(name1).addMembers(user.fullName());
+    gApi.groups().id(name2).addMembers(admin.fullName());
     gApi.groups().id(name1).addGroups(name2);
 
     this.g1 = gApi.groups().id(name1).detail();
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
index f5ebfea..47ac7a9 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -31,6 +31,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.truth.Correspondence;
+import com.google.common.truth.Correspondence.BinaryPredicate;
 import com.google.common.util.concurrent.AtomicLongMap;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.GerritConfig;
@@ -312,7 +313,7 @@
 
     List<? extends GroupAuditEventInfo> auditEvents = gApi.groups().id(group.get()).auditLog();
     assertThat(auditEvents).hasSize(1);
-    assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id, "Registered Users");
+    assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id(), "Registered Users");
   }
 
   @Test
@@ -412,7 +413,7 @@
 
   @Test
   public void createGroupWithoutCapability_Forbidden() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     gApi.groups().create(name("newGroup"));
   }
@@ -732,10 +733,10 @@
   @Test
   public void usersSeeTheirDirectMembershipWhenListingMembersRecursively() throws Exception {
     AccountGroup.UUID group = groupOperations.newGroup().create();
-    gApi.groups().id(group.get()).addMembers(user.username);
+    gApi.groups().id(group.get()).addMembers(user.username());
 
-    requestScopeOperations.setApiUser(user.getId());
-    assertMembers(gApi.groups().id(group.get()).members(true), user.fullName);
+    requestScopeOperations.setApiUser(user.id());
+    assertMembers(gApi.groups().id(group.get()).members(true), user.fullName());
   }
 
   @Test
@@ -743,9 +744,9 @@
     AccountGroup.UUID group1 = groupOperations.newGroup().ownerGroupUuid(adminGroupUuid()).create();
     AccountGroup.UUID group2 = groupOperations.newGroup().ownerGroupUuid(adminGroupUuid()).create();
     gApi.groups().id(group1.get()).addGroups(group2.get());
-    gApi.groups().id(group2.get()).addMembers(user.username);
+    gApi.groups().id(group2.get()).addMembers(user.username());
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     List<AccountInfo> listedMembers = gApi.groups().id(group1.get()).members(true);
 
     assertMembers(listedMembers);
@@ -757,11 +758,11 @@
     AccountGroup.UUID group1 = groupOperations.newGroup().ownerGroupUuid(ownerGroup).create();
     AccountGroup.UUID group2 = groupOperations.newGroup().ownerGroupUuid(ownerGroup).create();
     gApi.groups().id(group1.get()).addGroups(group2.get());
-    gApi.groups().id(group2.get()).addMembers(admin.username);
+    gApi.groups().id(group2.get()).addMembers(admin.username());
 
     List<AccountInfo> listedMembers = gApi.groups().id(group1.get()).members(true);
 
-    assertMembers(listedMembers, admin.fullName);
+    assertMembers(listedMembers, admin.fullName());
   }
 
   @Test
@@ -770,19 +771,19 @@
     AccountGroup.UUID group1 = groupOperations.newGroup().ownerGroupUuid(ownerGroup).create();
     AccountGroup.UUID group2 = groupOperations.newGroup().ownerGroupUuid(ownerGroup).create();
     gApi.groups().id(group1.get()).addGroups(group2.get());
-    gApi.groups().id(ownerGroup.get()).addMembers(user.username);
-    gApi.groups().id(group2.get()).addMembers(user.username);
+    gApi.groups().id(ownerGroup.get()).addMembers(user.username());
+    gApi.groups().id(group2.get()).addMembers(user.username());
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     List<AccountInfo> listedMembers = gApi.groups().id(group1.get()).members(true);
 
-    assertMembers(listedMembers, user.fullName);
+    assertMembers(listedMembers, user.fullName());
   }
 
   @Test
   public void defaultGroupsCreated() throws Exception {
     Iterable<String> names = gApi.groups().list().getAsMap().keySet();
-    assertThat(names).containsAllOf("Administrators", "Non-Interactive Users").inOrder();
+    assertThat(names).containsAtLeast("Administrators", "Non-Interactive Users").inOrder();
   }
 
   @Test
@@ -834,13 +835,13 @@
     in.ownerId = adminGroupUuid().get();
     gApi.groups().create(in);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertThat(gApi.groups().list().getAsMap()).doesNotContainKey(newGroupName);
 
-    requestScopeOperations.setApiUser(admin.getId());
-    gApi.groups().id(newGroupName).addMembers(user.username);
+    requestScopeOperations.setApiUser(admin.id());
+    gApi.groups().id(newGroupName).addMembers(user.username());
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertThat(gApi.groups().list().getAsMap()).containsKey(newGroupName);
   }
 
@@ -913,41 +914,41 @@
     GroupApi g = gApi.groups().create(name("group"));
     List<? extends GroupAuditEventInfo> auditEvents = g.auditLog();
     assertThat(auditEvents).hasSize(1);
-    assertMemberAuditEvent(auditEvents.get(0), Type.ADD_USER, admin.id, admin.id);
+    assertMemberAuditEvent(auditEvents.get(0), Type.ADD_USER, admin.id(), admin.id());
 
-    g.addMembers(user.username);
+    g.addMembers(user.username());
     auditEvents = g.auditLog();
     assertThat(auditEvents).hasSize(2);
-    assertMemberAuditEvent(auditEvents.get(0), Type.ADD_USER, admin.id, user.id);
+    assertMemberAuditEvent(auditEvents.get(0), Type.ADD_USER, admin.id(), user.id());
 
-    g.removeMembers(user.username);
+    g.removeMembers(user.username());
     auditEvents = g.auditLog();
     assertThat(auditEvents).hasSize(3);
-    assertMemberAuditEvent(auditEvents.get(0), Type.REMOVE_USER, admin.id, user.id);
+    assertMemberAuditEvent(auditEvents.get(0), Type.REMOVE_USER, admin.id(), user.id());
 
     String otherGroup = name("otherGroup");
     gApi.groups().create(otherGroup);
     g.addGroups(otherGroup);
     auditEvents = g.auditLog();
     assertThat(auditEvents).hasSize(4);
-    assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id, otherGroup);
+    assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id(), otherGroup);
 
     g.removeGroups(otherGroup);
     auditEvents = g.auditLog();
     assertThat(auditEvents).hasSize(5);
-    assertSubgroupAuditEvent(auditEvents.get(0), Type.REMOVE_GROUP, admin.id, otherGroup);
+    assertSubgroupAuditEvent(auditEvents.get(0), Type.REMOVE_GROUP, admin.id(), otherGroup);
 
     // Add a removed member back again.
-    g.addMembers(user.username);
+    g.addMembers(user.username());
     auditEvents = g.auditLog();
     assertThat(auditEvents).hasSize(6);
-    assertMemberAuditEvent(auditEvents.get(0), Type.ADD_USER, admin.id, user.id);
+    assertMemberAuditEvent(auditEvents.get(0), Type.ADD_USER, admin.id(), user.id());
 
     // Add a removed group back again.
     g.addGroups(otherGroup);
     auditEvents = g.auditLog();
     assertThat(auditEvents).hasSize(7);
-    assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id, otherGroup);
+    assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id(), otherGroup);
 
     Timestamp lastDate = null;
     for (GroupAuditEventInfo auditEvent : auditEvents) {
@@ -979,7 +980,7 @@
     List<? extends GroupAuditEventInfo> auditEvents = gApi.groups().id(parentGroup.id).auditLog();
     assertThat(auditEvents).hasSize(2);
     // Verify the unavailable subgroup's name is null.
-    assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id, null);
+    assertSubgroupAuditEvent(auditEvents.get(0), Type.ADD_GROUP, admin.id(), null);
   }
 
   private void deleteGroupRef(String groupId) throws Exception {
@@ -1008,20 +1009,20 @@
     TestAccount groupOwner = accountCreator.user2();
     GroupInput in = new GroupInput();
     in.name = name("group");
-    in.members = Stream.of(groupOwner).map(u -> u.id.toString()).collect(toList());
+    in.members = Stream.of(groupOwner).map(u -> u.id().toString()).collect(toList());
     in.visibleToAll = true;
     GroupInfo group = gApi.groups().create(in).get();
 
     // admin can reindex any group
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.groups().id(group.id).index();
 
     // group owner can reindex own group (group is owned by itself)
-    requestScopeOperations.setApiUser(groupOwner.getId());
+    requestScopeOperations.setApiUser(groupOwner.id());
     gApi.groups().id(group.id).index();
 
     // user cannot reindex any group
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("not allowed to index group");
     gApi.groups().id(group.id).index();
@@ -1093,7 +1094,7 @@
     repo.reset("groupRef");
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), repo, "Update group", "arbitraryFile.txt", "some content")
+            .create(admin.newIdent(), repo, "Update group", "arbitraryFile.txt", "some content")
             .to(groupRefName);
     if (expectedErrorOnUpdate != null) {
       r.assertErrorStatus(expectedErrorOnUpdate);
@@ -1112,7 +1113,7 @@
     TestRepository<InMemoryRepository> repo = cloneProject(project);
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), repo, "Update group", "arbitraryFile.txt", "some content")
+            .create(admin.newIdent(), repo, "Update group", "arbitraryFile.txt", "some content")
             .setParents(ImmutableList.of())
             .to(RefNames.REFS_GROUPS + name("bar"));
     r.assertOkStatus();
@@ -1150,7 +1151,7 @@
 
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), repo, "Subject", "project.config", config)
+            .create(admin.newIdent(), repo, "Subject", "project.config", config)
             .to(RefNames.REFS_CONFIG);
     r.assertErrorStatus("invalid project configuration");
     r.assertMessage("All-Users must inherit from All-Projects");
@@ -1200,7 +1201,7 @@
     grant(allUsers, refPattern, Permission.PUSH);
 
     TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
-    PushOneCommit.Result r = pushFactory.create(admin.getIdent(), allUsersRepo).to(groupRef);
+    PushOneCommit.Result r = pushFactory.create(admin.newIdent(), allUsersRepo).to(groupRef);
     r.assertErrorStatus();
     assertThat(r.getMessage()).contains("Not allowed to create group branch.");
 
@@ -1299,12 +1300,12 @@
   public void groupsOfUserCanBeListedInSlaveMode() throws Exception {
     GroupInput groupInput = new GroupInput();
     groupInput.name = name("contributors");
-    groupInput.members = ImmutableList.of(user.username);
+    groupInput.members = ImmutableList.of(user.username());
     gApi.groups().create(groupInput).get();
     restartAsSlave();
 
-    requestScopeOperations.setApiUser(user.getId());
-    List<GroupInfo> groups = gApi.groups().list().withUser(user.username).get();
+    requestScopeOperations.setApiUser(user.id());
+    List<GroupInfo> groups = gApi.groups().list().withUser(user.username()).get();
     ImmutableList<String> groupNames =
         groups.stream().map(group -> group.name).collect(toImmutableList());
     assertThat(groupNames).contains(groupInput.name);
@@ -1393,18 +1394,15 @@
   }
 
   private static Correspondence<AccountInfo, String> getAccountToUsernameCorrespondence() {
-    return new Correspondence<AccountInfo, String>() {
-      @Override
-      public boolean compare(AccountInfo actualAccount, String expectedName) {
-        String username = actualAccount == null ? null : actualAccount.username;
-        return Objects.equals(username, expectedName);
-      }
-
-      @Override
-      public String toString() {
-        return "has username";
-      }
-    };
+    return Correspondence.from(
+        new BinaryPredicate<AccountInfo, String>() {
+          @Override
+          public boolean apply(AccountInfo actualAccount, String expectedName) {
+            String username = actualAccount == null ? null : actualAccount.username;
+            return Objects.equals(username, expectedName);
+          }
+        },
+        "has username");
   }
 
   private void assertStaleGroupAndReindex(AccountGroup.UUID groupUuid) throws IOException {
@@ -1429,7 +1427,7 @@
 
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), repo, "Update group config", "group.config", "some content")
+            .create(admin.newIdent(), repo, "Update group config", "group.config", "some content")
             .to(MagicBranch.NEW_CHANGE + groupRef);
     r.assertOkStatus();
     assertThat(r.getChange().change().getDest().get()).isEqualTo(groupRef);
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
index 7056312..5e143c0 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
@@ -18,7 +18,8 @@
 
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.errors.NoSuchGroupException;
+import com.google.gerrit.exceptions.NoSuchGroupException;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.server.ServerInitiated;
@@ -27,7 +28,6 @@
 import com.google.gerrit.server.group.db.InternalGroupCreation;
 import com.google.gerrit.server.group.db.InternalGroupUpdate;
 import com.google.gerrit.testing.InMemoryTestEnvironment;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -92,7 +92,7 @@
   }
 
   private void createGroup(InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
-      throws OrmException, IOException, ConfigInvalidException {
+      throws IOException, ConfigInvalidException {
     groupsUpdateProvider.get().createGroup(groupCreation, groupUpdate);
   }
 
@@ -138,7 +138,7 @@
       InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().build();
       try {
         groupsUpdateProvider.get().createGroup(groupCreation, groupUpdate);
-      } catch (OrmException | IOException | ConfigInvalidException e) {
+      } catch (StorageException | IOException | ConfigInvalidException e) {
         throw new IllegalStateException(e);
       }
     }
diff --git a/javatests/com/google/gerrit/acceptance/api/plugin/PluginIT.java b/javatests/com/google/gerrit/acceptance/api/plugin/PluginIT.java
index 752d5f1..1d3eb17 100644
--- a/javatests/com/google/gerrit/acceptance/api/plugin/PluginIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/plugin/PluginIT.java
@@ -116,7 +116,7 @@
     deprecatedInput();
 
     // Non-admin cannot disable
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     try {
       gApi.plugins().name("plugin-a").disable();
       fail("Expected AuthException");
diff --git a/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java b/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
index 4088ebf..8cdd2f66 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/CheckAccessIT.java
@@ -60,7 +60,7 @@
         groupOperations.newGroup().name(name("privilegedGroup")).create();
 
     privilegedUser = accountCreator.create("privilegedUser", "snowden@nsa.gov", "Ed Snowden");
-    groupOperations.group(privilegedGroupUuid).forUpdate().addMember(privilegedUser.id).update();
+    groupOperations.group(privilegedGroupUuid).forUpdate().addMember(privilegedUser.id()).update();
 
     try (ProjectConfigUpdate u = updateProject(secretProject)) {
       ProjectConfig cfg = u.getConfig();
@@ -97,7 +97,7 @@
   @Test
   public void nonexistentPermission() throws Exception {
     AccessCheckInput in = new AccessCheckInput();
-    in.account = user.email;
+    in.account = user.email();
     in.permission = "notapermission";
     in.ref = "refs/heads/master";
 
@@ -109,7 +109,7 @@
   @Test
   public void permissionLacksRef() throws Exception {
     AccessCheckInput in = new AccessCheckInput();
-    in.account = user.email;
+    in.account = user.email();
     in.permission = "forge_author";
 
     exception.expect(BadRequestException.class);
@@ -120,7 +120,7 @@
   @Test
   public void changePermission() throws Exception {
     AccessCheckInput in = new AccessCheckInput();
-    in.account = user.email;
+    in.account = user.email();
     in.permission = "rebase";
     in.ref = "refs/heads/master";
 
@@ -187,7 +187,7 @@
                 + normalProject.get()
                 + "/check.access"
                 + "?ref=refs/heads/master&perm=viewPrivateChanges&account="
-                + user.email);
+                + user.email());
     rep.assertOK();
     assertThat(rep.getEntityContent()).contains("403");
   }
@@ -197,28 +197,28 @@
     List<TestCase> inputs =
         ImmutableList.of(
             TestCase.projectRefPerm(
-                user.email,
+                user.email(),
                 normalProject.get(),
                 "refs/heads/master",
                 Permission.VIEW_PRIVATE_CHANGES,
                 403),
-            TestCase.project(user.email, normalProject.get(), 200),
-            TestCase.project(user.email, secretProject.get(), 403),
+            TestCase.project(user.email(), normalProject.get(), 200),
+            TestCase.project(user.email(), secretProject.get(), 403),
             TestCase.projectRef(
-                user.email, secretRefProject.get(), "refs/heads/secret/master", 403),
+                user.email(), secretRefProject.get(), "refs/heads/secret/master", 403),
             TestCase.projectRef(
-                privilegedUser.email, secretRefProject.get(), "refs/heads/secret/master", 200),
-            TestCase.projectRef(privilegedUser.email, normalProject.get(), null, 200),
-            TestCase.projectRef(privilegedUser.email, secretProject.get(), null, 200),
-            TestCase.projectRef(privilegedUser.email, secretProject.get(), null, 200),
+                privilegedUser.email(), secretRefProject.get(), "refs/heads/secret/master", 200),
+            TestCase.projectRef(privilegedUser.email(), normalProject.get(), null, 200),
+            TestCase.projectRef(privilegedUser.email(), secretProject.get(), null, 200),
+            TestCase.projectRef(privilegedUser.email(), secretProject.get(), null, 200),
             TestCase.projectRefPerm(
-                privilegedUser.email,
+                privilegedUser.email(),
                 normalProject.get(),
                 "refs/heads/master",
                 Permission.VIEW_PRIVATE_CHANGES,
                 200),
             TestCase.projectRefPerm(
-                privilegedUser.email,
+                privilegedUser.email(),
                 normalProject.get(),
                 "refs/heads/master",
                 Permission.FORGE_SERVER,
@@ -266,7 +266,7 @@
       assertThat(u.delete()).isEqualTo(Result.FORCED);
     }
     AccessCheckInput input = new AccessCheckInput();
-    input.account = privilegedUser.email;
+    input.account = privilegedUser.email();
 
     AccessCheckInfo info = gApi.projects().name(normalProject.get()).checkAccess(input);
     assertThat(info.status).isEqualTo(200);
diff --git a/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java b/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java
index 3b2df79..388ea30 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java
@@ -280,8 +280,8 @@
             .branch("HEAD")
             .commit()
             .message("A change")
-            .author(admin.getIdent())
-            .committer(new PersonIdent(admin.getIdent(), testRepo.getDate()))
+            .author(admin.newIdent())
+            .committer(new PersonIdent(admin.newIdent(), testRepo.getDate()))
             .create();
     pushHead(testRepo, "refs/for/master");
     return commit;
diff --git a/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java b/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
index 9267bc3..77350b0 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
@@ -353,7 +353,7 @@
   @Test
   public void nonOwnerCannotSetConfig() throws Exception {
     ConfigInput input = createTestConfigInput();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("write refs/meta/config not permitted");
     gApi.projects().name(project.get()).config(input);
@@ -388,7 +388,7 @@
   @Test
   public void setHeadNotAllowed() throws Exception {
     gApi.projects().name(project.get()).branch("test").create(new BranchInput());
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("not permitted: set HEAD on refs/heads/test");
     gApi.projects().name(project.get()).head("test");
diff --git a/javatests/com/google/gerrit/acceptance/api/project/SetParentIT.java b/javatests/com/google/gerrit/acceptance/api/project/SetParentIT.java
index e428e89..3c1428d 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/SetParentIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/SetParentIT.java
@@ -41,7 +41,7 @@
   @Test
   public void setParentNotAllowed() throws Exception {
     String parent = projectOperations.newProject().create().get();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     gApi.projects().name(project.get()).parent(parent);
   }
@@ -50,7 +50,7 @@
   @GerritConfig(name = "receive.allowProjectOwnersToChangeParent", value = "true")
   public void setParentNotAllowedForNonOwners() throws Exception {
     String parent = projectOperations.newProject().create().get();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     gApi.projects().name(project.get()).parent(parent);
   }
@@ -74,7 +74,7 @@
   @GerritConfig(name = "receive.allowProjectOwnersToChangeParent", value = "true")
   public void setParentAllowedForOwners() throws Exception {
     String parent = projectOperations.newProject().create().get();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     grant(project, "refs/*", Permission.OWNER, false, SystemGroupBackend.REGISTERED_USERS);
     gApi.projects().name(project.get()).parent(parent);
     assertThat(gApi.projects().name(project.get()).parent()).isEqualTo(parent);
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
index b6969a2..cbfc09f 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
@@ -415,7 +415,7 @@
         .content()
         .onlyElement()
         .commonLines()
-        .containsAllOf("Line 1", "Line 2", "Line 3")
+        .containsExactly("Line 1", "Line 2", "Line 3", "")
         .inOrder();
     assertThat(diffInfo).content().onlyElement().linesOfA().isNull();
     assertThat(diffInfo).content().onlyElement().linesOfB().isNull();
@@ -2363,7 +2363,7 @@
         .content()
         .element(0)
         .commonLines()
-        .containsAllOf("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
+        .containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
         .inOrder();
   }
 
@@ -2389,7 +2389,7 @@
         .content()
         .element(0)
         .commonLines()
-        .containsAllOf("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
+        .containsAtLeast("Line 1", "Line two", "Line 3", "Line 4", "Line 5")
         .inOrder();
   }
 
@@ -2521,7 +2521,7 @@
       throws Exception {
     testRepo.reset(parentCommit);
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), testRepo, "Adjust files of repo", files);
+        pushFactory.create(admin.newIdent(), testRepo, "Adjust files of repo", files);
     PushOneCommit.Result result = push.to("refs/for/master");
     return result.getCommit();
   }
@@ -2545,7 +2545,7 @@
         Arrays.stream(removedFilePaths)
             .collect(toMap(Function.identity(), path -> "Irrelevant content"));
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), testRepo, "Remove files from repo", files);
+        pushFactory.create(admin.newIdent(), testRepo, "Remove files from repo", files);
     PushOneCommit.Result result = push.rm("refs/for/master");
     return result.getCommit();
   }
@@ -2564,7 +2564,7 @@
 
   private Result createEmptyChange() throws Exception {
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), testRepo, "Test change", ImmutableMap.of());
+        pushFactory.create(admin.newIdent(), testRepo, "Test change", ImmutableMap.of());
     return push.to("refs/for/master");
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index 7a0fff8..fc65eca 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -218,24 +218,24 @@
     PushOneCommit.Result r = createChange();
     String changeId = project.get() + "~master~" + r.getChangeId();
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     revision(r).review(ReviewInput.approve());
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     revision(r).review(ReviewInput.recommend());
 
-    requestScopeOperations.setApiUser(admin.getId());
-    gApi.changes().id(changeId).reviewer(user.username).deleteVote("Code-Review");
+    requestScopeOperations.setApiUser(admin.id());
+    gApi.changes().id(changeId).reviewer(user.username()).deleteVote("Code-Review");
     Optional<ApprovalInfo> crUser =
         get(changeId, DETAILED_LABELS).labels.get("Code-Review").all.stream()
-            .filter(a -> a._accountId == user.id.get())
+            .filter(a -> a._accountId == user.id().get())
             .findFirst();
     assertThat(crUser).isPresent();
     assertThat(crUser.get().value).isEqualTo(0);
 
     revision(r).submit();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     ReviewInput in = new ReviewInput();
     in.label("Code-Review", 1);
     in.message = "Still LGTM";
@@ -243,7 +243,7 @@
 
     ApprovalInfo cr =
         gApi.changes().id(changeId).get(DETAILED_LABELS).labels.get("Code-Review").all.stream()
-            .filter(a -> a._accountId == user.getId().get())
+            .filter(a -> a._accountId == user.id().get())
             .findFirst()
             .get();
     assertThat(cr.postSubmit).isTrue();
@@ -295,7 +295,7 @@
   @Test
   public void voteNotAllowedWithoutPermission() throws Exception {
     PushOneCommit.Result r = createChange();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("is restricted");
     gApi.changes().id(r.getChange().getId().get()).current().review(ReviewInput.approve());
@@ -412,7 +412,7 @@
     String subject = "Test change\n\nChange-Id: Ideadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(), testRepo, subject, "another_file.txt", "another content");
+            admin.newIdent(), testRepo, subject, "another_file.txt", "another content");
     PushOneCommit.Result r2 = push.to("refs/for/master");
 
     // Change 2's parent should be change 1
@@ -469,7 +469,7 @@
 
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             PushOneCommit.FILE_NAME,
@@ -495,7 +495,7 @@
     String destContent = "some content";
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             ImmutableMap.of(PushOneCommit.FILE_NAME, destContent, "foo.txt", "foo"));
@@ -506,7 +506,7 @@
     String changeContent = "another content";
     push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             ImmutableMap.of(PushOneCommit.FILE_NAME, changeContent, "bar.txt", "bar"));
@@ -577,15 +577,14 @@
             "Patch Set 1: Cherry Picked from branch master.\n\n"
                 + "The following files contain Git conflicts:\n"
                 + "* "
-                + PushOneCommit.FILE_NAME
-                + "\n");
+                + PushOneCommit.FILE_NAME);
   }
 
   @Test
   public void cherryPickToExistingChange() throws Exception {
     PushOneCommit.Result r1 =
         pushFactory
-            .create(admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "a")
+            .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "a")
             .to("refs/for/master");
     String t1 = project.get() + "~master~" + r1.getChangeId();
 
@@ -595,7 +594,7 @@
 
     PushOneCommit.Result r2 =
         pushFactory
-            .create(admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "b", r1.getChangeId())
+            .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "b", r1.getChangeId())
             .to("refs/for/foo");
     String t2 = project.get() + "~foo~" + r2.getChangeId();
     gApi.changes().id(t2).abandon();
@@ -729,7 +728,7 @@
 
     // 'user' cherry-picks the change to a new branch, the source change's author/committer('admin')
     // will be added as a reviewer of the newly created change.
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     CherryPickInput input = new CherryPickInput();
     input.message = "it goes to a new branch";
 
@@ -752,7 +751,7 @@
     input.destination = "branch-3";
     input.notify = NotifyHandling.NONE;
     input.notifyDetails =
-        ImmutableMap.of(RecipientType.TO, new NotifyInfo(ImmutableList.of(userToNotify.email)));
+        ImmutableMap.of(RecipientType.TO, new NotifyInfo(ImmutableList.of(userToNotify.email())));
     sender.clear();
     gApi.changes().id(changeId).current().cherryPick(input);
     assertNotifyTo(userToNotify);
@@ -765,13 +764,13 @@
     // Change is created by 'admin'.
     PushOneCommit.Result r = createChange();
     // Change is approved by 'admin2'. Change is CC'd to 'user'.
-    requestScopeOperations.setApiUser(accountCreator.admin2().getId());
+    requestScopeOperations.setApiUser(accountCreator.admin2().id());
     ReviewInput in = ReviewInput.approve();
-    in.reviewer(user.email, ReviewerState.CC, true);
+    in.reviewer(user.email(), ReviewerState.CC, true);
     gApi.changes().id(r.getChangeId()).current().review(in);
 
     // Change is cherrypicked by 'user2'.
-    requestScopeOperations.setApiUser(accountCreator.user2().getId());
+    requestScopeOperations.setApiUser(accountCreator.user2().id());
     CherryPickInput cin = new CherryPickInput();
     cin.message = "this need to go to stable";
     cin.destination = "stable";
@@ -788,8 +787,8 @@
     assertThat(result).containsKey(ReviewerState.CC);
     List<Integer> ccs =
         result.get(ReviewerState.CC).stream().map(a -> a._accountId).collect(toList());
-    assertThat(ccs).containsExactly(user.id.get());
-    assertThat(reviewers).containsExactly(admin.id.get(), accountCreator.admin2().id.get());
+    assertThat(ccs).containsExactly(user.id().get());
+    assertThat(reviewers).containsExactly(admin.id().get(), accountCreator.admin2().id().get());
   }
 
   @Test
@@ -850,7 +849,7 @@
     input.base = dstChange.getCommit().name();
     input.message = srcChange.getCommit().getFullMessage();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(UnprocessableEntityException.class);
     exception.expectMessage(
         String.format("Commit %s does not exist on branch refs/heads/foo", input.base));
@@ -910,11 +909,11 @@
 
   @Test
   public void canRebase() throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result r1 = push.to("refs/for/master");
     merge(r1);
 
-    push = pushFactory.create(admin.getIdent(), testRepo);
+    push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result r2 = push.to("refs/for/master");
     boolean canRebase =
         gApi.changes().id(r2.getChangeId()).revision(r2.getCommit().name()).canRebase();
@@ -922,7 +921,7 @@
     merge(r2);
 
     testRepo.reset(r1.getCommit());
-    push = pushFactory.create(admin.getIdent(), testRepo);
+    push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result r3 = push.to("refs/for/master");
 
     canRebase = gApi.changes().id(r3.getChangeId()).revision(r3.getCommit().name()).canRebase();
@@ -931,7 +930,7 @@
 
   @Test
   public void setUnsetReviewedFlag() throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result r = push.to("refs/for/master");
 
     gApi.changes().id(r.getChangeId()).current().setReviewed(PushOneCommit.FILE_NAME, true);
@@ -946,7 +945,7 @@
 
   @Test
   public void setUnsetReviewedFlagByFileApi() throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result r = push.to("refs/for/master");
 
     gApi.changes().id(r.getChangeId()).current().file(PushOneCommit.FILE_NAME).setReviewed(true);
@@ -965,7 +964,7 @@
 
     PushOneCommit push1 =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             PushOneCommit.FILE_NAME,
@@ -980,7 +979,7 @@
 
     PushOneCommit push2 =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             PushOneCommit.FILE_NAME,
@@ -1099,7 +1098,7 @@
   public void queryRevisionFiles() throws Exception {
     Map<String, String> files = ImmutableMap.of("file1.txt", "content 1", "file2.txt", "content 2");
     PushOneCommit.Result result =
-        pushFactory.create(admin.getIdent(), testRepo, SUBJECT, files).to("refs/for/master");
+        pushFactory.create(admin.newIdent(), testRepo, SUBJECT, files).to("refs/for/master");
     result.assertOkStatus();
     String changeId = result.getChangeId();
 
@@ -1131,7 +1130,7 @@
   public void setDescriptionNotAllowedWithoutPermission() throws Exception {
     PushOneCommit.Result r = createChange();
     assertDescription(r, "");
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("edit description not permitted");
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).description("test");
@@ -1142,7 +1141,7 @@
     PushOneCommit.Result r = createChange();
     assertDescription(r, "");
     grant(project, "refs/heads/master", Permission.OWNER, false, REGISTERED_USERS);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).description("test");
     assertDescription(r, "test");
   }
@@ -1266,7 +1265,7 @@
                 .get()
                 .author
                 .email)
-        .isEqualTo(admin.email);
+        .isEqualTo(admin.email());
 
     draftApi.delete();
     assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).drafts())
@@ -1292,7 +1291,7 @@
     assertThat(out).hasSize(1);
     CommentInfo comment = Iterables.getOnlyElement(out.get(FILE_NAME));
     assertThat(comment.message).isEqualTo(in.message);
-    assertThat(comment.author.email).isEqualTo(admin.email);
+    assertThat(comment.author.email).isEqualTo(admin.email());
     assertThat(comment.path).isNull();
 
     List<CommentInfo> list =
@@ -1405,17 +1404,17 @@
     amendChange(r.getChangeId());
 
     // code-review
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     recommend(r.getChangeId());
 
     // check if it's blocked to delete a vote on a non-current patch set.
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     exception.expect(MethodNotAllowedException.class);
     exception.expectMessage("Cannot access on non-current patch set");
     gApi.changes()
         .id(r.getChangeId())
         .revision(r.getCommit().getName())
-        .reviewer(user.getId().toString())
+        .reviewer(user.id().toString())
         .deleteVote("Code-Review");
   }
 
@@ -1428,27 +1427,27 @@
     amendChange(r.getChangeId());
 
     // code-review
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     recommend(r.getChangeId());
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes()
         .id(r.getChangeId())
         .current()
-        .reviewer(user.getId().toString())
+        .reviewer(user.id().toString())
         .deleteVote("Code-Review");
 
     Map<String, Short> m =
-        gApi.changes().id(r.getChangeId()).current().reviewer(user.getId().toString()).votes();
+        gApi.changes().id(r.getChangeId()).current().reviewer(user.id().toString()).votes();
 
     assertThat(m).containsExactly("Code-Review", Short.valueOf((short) 0));
 
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
     ChangeMessageInfo message = Iterables.getLast(c.messages);
-    assertThat(message.author._accountId).isEqualTo(admin.getId().get());
+    assertThat(message.author._accountId).isEqualTo(admin.id().get());
     assertThat(message.message).isEqualTo("Removed Code-Review+1 by User <user@example.com>\n");
     assertThat(getReviewers(c.reviewers.get(ReviewerState.REVIEWER)))
-        .containsExactlyElementsIn(ImmutableSet.of(admin.getId(), user.getId()));
+        .containsExactlyElementsIn(ImmutableSet.of(admin.id(), user.id()));
   }
 
   @Test
@@ -1463,22 +1462,22 @@
     List<ApprovalInfo> approvals = votes.get("Code-Review");
     assertThat(approvals).hasSize(1);
     ApprovalInfo approval = approvals.get(0);
-    assertThat(approval._accountId).isEqualTo(admin.id.get());
-    assertThat(approval.email).isEqualTo(admin.email);
-    assertThat(approval.username).isEqualTo(admin.username);
+    assertThat(approval._accountId).isEqualTo(admin.id().get());
+    assertThat(approval.email).isEqualTo(admin.email());
+    assertThat(approval.username).isEqualTo(admin.username());
 
     // Also vote on it with another user
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(changeId).current().review(ReviewInput.dislike());
 
     // Patch set 1 has 2 votes on Code-Review
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     votes = gApi.changes().id(changeId).current().votes();
     assertThat(votes.keySet()).containsExactly("Code-Review");
     approvals = votes.get("Code-Review");
     assertThat(approvals).hasSize(2);
     assertThat(approvals.stream().map(a -> a._accountId))
-        .containsExactlyElementsIn(ImmutableList.of(admin.id.get(), user.id.get()));
+        .containsExactlyElementsIn(ImmutableList.of(admin.id().get(), user.id().get()));
 
     // Create a new patch set which does not have any votes
     amendChange(changeId);
@@ -1506,7 +1505,7 @@
       throws Exception {
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(), testRepo, "test commit", "a.txt", content, r.getChangeId());
+            admin.newIdent(), testRepo, "test commit", "a.txt", content, r.getChangeId());
     return push.to("refs/for/master");
   }
 
@@ -1532,18 +1531,18 @@
 
     PushOneCommit.Result changeAResult =
         pushFactory
-            .create(admin.getIdent(), testRepo, "change a", parent1FileName, "Content of a")
+            .create(admin.newIdent(), testRepo, "change a", parent1FileName, "Content of a")
             .to("refs/for/" + branchAName);
 
     testRepo.reset(initialCommit);
     PushOneCommit.Result changeBResult =
         pushFactory
-            .create(admin.getIdent(), testRepo, "change b", parent2FileName, "Content of b")
+            .create(admin.newIdent(), testRepo, "change b", parent2FileName, "Content of b")
             .to("refs/for/" + branchBName);
 
     PushOneCommit pushableMergeCommit =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             "merge",
             ImmutableMap.of(parent1FileName, "Content of a", parent2FileName, "Content of b"));
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
index 2f23123..ba228f6 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RobotCommentsIT.java
@@ -67,7 +67,7 @@
   public void setUp() throws Exception {
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             "Provide files which can be used for fixes",
             ImmutableMap.of(FILE_NAME, FILE_CONTENT, FILE_NAME2, FILE_CONTENT2));
@@ -105,7 +105,7 @@
     RobotCommentInput in = createRobotCommentInput();
     addRobotComment(changeId, in);
 
-    pushFactory.create(admin.getIdent(), testRepo, changeId).to("refs/for/master");
+    pushFactory.create(admin.newIdent(), testRepo, changeId).to("refs/for/master");
 
     RobotCommentInput in2 = createRobotCommentInput();
     addRobotComment(changeId, in2);
@@ -917,7 +917,7 @@
     PushOneCommit.Result r1 = createChange();
     PushOneCommit.Result r2 =
         pushFactory
-            .create(admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new content", r1.getChangeId())
+            .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "new content", r1.getChangeId())
             .to("refs/for/master");
 
     addRobotComment(r2.getChangeId(), createRobotCommentInputWithMandatoryFields());
@@ -1006,7 +1006,7 @@
     assertThat(c.line).isEqualTo(expected.line);
     assertThat(c.message).isEqualTo(expected.message);
 
-    assertThat(c.author.email).isEqualTo(admin.email);
+    assertThat(c.author.email).isEqualTo(admin.email());
 
     if (expectPath) {
       assertThat(c.path).isEqualTo(expected.path);
diff --git a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
index 8b5975d..6827219 100644
--- a/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
+++ b/javatests/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -111,11 +111,11 @@
 
   @Before
   public void setUp() throws Exception {
-    changeId = newChange(admin.getIdent());
+    changeId = newChange(admin.newIdent());
     ps = getCurrentPatchSet(changeId);
     assertThat(ps).isNotNull();
-    amendChange(admin.getIdent(), changeId);
-    changeId2 = newChange2(admin.getIdent());
+    amendChange(admin.newIdent(), changeId);
+    changeId2 = newChange2(admin.newIdent());
   }
 
   @Test
@@ -139,7 +139,7 @@
   @Test
   public void deleteEditOfOlderPatchSet() throws Exception {
     createArbitraryEditFor(changeId2);
-    amendChange(admin.getIdent(), changeId2);
+    amendChange(admin.newIdent(), changeId2);
 
     gApi.changes().id(changeId2).edit().delete();
     assertThat(getEdit(changeId2)).isAbsent();
@@ -199,7 +199,7 @@
   @Test
   public void publishEditNotifyRest() throws Exception {
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     gApi.changes().id(changeId).addReviewer(in);
 
     createArbitraryEditFor(changeId);
@@ -214,7 +214,7 @@
   @Test
   public void publishEditWithDefaultNotify() throws Exception {
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     gApi.changes().id(changeId).addReviewer(in);
 
     createArbitraryEditFor(changeId);
@@ -236,7 +236,7 @@
     PatchSet previousPatchSet = getCurrentPatchSet(changeId2);
     createEmptyEditFor(changeId2);
     gApi.changes().id(changeId2).edit().modifyFile(FILE_NAME, RawInputUtil.create(CONTENT_NEW));
-    amendChange(admin.getIdent(), changeId2);
+    amendChange(admin.newIdent(), changeId2);
     PatchSet currentPatchSet = getCurrentPatchSet(changeId2);
 
     Optional<EditInfo> originalEdit = getEdit(changeId2);
@@ -255,7 +255,7 @@
     PatchSet previousPatchSet = getCurrentPatchSet(changeId2);
     createEmptyEditFor(changeId2);
     gApi.changes().id(changeId2).edit().modifyFile(FILE_NAME, RawInputUtil.create(CONTENT_NEW));
-    amendChange(admin.getIdent(), changeId2);
+    amendChange(admin.newIdent(), changeId2);
     PatchSet currentPatchSet = getCurrentPatchSet(changeId2);
 
     Optional<EditInfo> originalEdit = getEdit(changeId2);
@@ -278,7 +278,7 @@
     assertThat(edit).value().baseRevision().isEqualTo(currentPatchSet.getRevision().get());
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             FILE_NAME,
@@ -302,7 +302,7 @@
   public void updateRootCommitMessage() throws Exception {
     // Re-clone empty repo; TestRepository doesn't let us reset to unborn head.
     testRepo = cloneProject(project);
-    changeId = newChange(admin.getIdent());
+    changeId = newChange(admin.newIdent());
 
     createEmptyEditFor(changeId);
     Optional<EditInfo> edit = getEdit(changeId);
@@ -628,11 +628,11 @@
     gApi.changes().id(changeId2).edit().publish(publishInput);
     assertThat(queryEdits()).isEmpty();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     createEmptyEditFor(changeId);
     assertThat(queryEdits()).hasSize(1);
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     assertThat(queryEdits()).isEmpty();
   }
 
@@ -681,7 +681,7 @@
     block(p, "refs/for/*", Permission.ADD_PATCH_SET, REGISTERED_USERS);
 
     // Create change as user
-    PushOneCommit push = pushFactory.create(user.getIdent(), userTestRepo);
+    PushOneCommit push = pushFactory.create(user.newIdent(), userTestRepo);
     PushOneCommit.Result r1 = push.to("refs/for/master");
     r1.assertOkStatus();
 
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 22af233..899b09a 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -169,10 +169,10 @@
 
   @After
   public void resetPublishCommentOnPushOption() throws Exception {
-    requestScopeOperations.setApiUser(admin.getId());
-    GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id.get()).getPreferences();
+    requestScopeOperations.setApiUser(admin.id());
+    GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id().get()).getPreferences();
     prefs.publishCommentsOnPush = false;
-    gApi.accounts().id(admin.id.get()).setPreferences(prefs);
+    gApi.accounts().id(admin.id().get()).setPreferences(prefs);
   }
 
   protected void selectProtocol(Protocol p) throws Exception {
@@ -320,8 +320,8 @@
         testRepo
             .commit()
             .message("Initial commit")
-            .author(admin.getIdent())
-            .committer(admin.getIdent())
+            .author(admin.newIdent())
+            .committer(admin.newIdent())
             .insertChangeId()
             .create();
     String id = GitUtil.getChangeId(testRepo, c).get();
@@ -353,8 +353,8 @@
         testRepo
             .commit()
             .message("Initial commit")
-            .author(admin.getIdent())
-            .committer(admin.getIdent())
+            .author(admin.newIdent())
+            .committer(admin.newIdent())
             .insertChangeId()
             .create();
     testRepo.reset(c);
@@ -389,7 +389,7 @@
         .create();
     PushOneCommit.Result r2 =
         pushFactory
-            .create(admin.getIdent(), testRepo, "another commit", "b.txt", "bbb")
+            .create(admin.newIdent(), testRepo, "another commit", "b.txt", "bbb")
             .to("refs/for/master");
     Change.Id id2 = r2.getChange().getId();
     r2.assertOkStatus();
@@ -439,8 +439,8 @@
         .branch("HEAD")
         .commit()
         .message("A change")
-        .author(admin.getIdent())
-        .committer(new PersonIdent(admin.getIdent(), testRepo.getDate()))
+        .author(admin.newIdent())
+        .committer(new PersonIdent(admin.newIdent(), testRepo.getDate()))
         .create();
     PushResult result = pushHead(testRepo, "refs/for/master");
     assertThat(result.getMessages()).contains("warning: pushing without Change-Id is deprecated");
@@ -493,7 +493,7 @@
     List<String> pushOptions = new ArrayList<>();
     pushOptions.add(topicOption);
 
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     push.setPushOptions(pushOptions);
     PushOneCommit.Result r = push.to("refs/for/master");
 
@@ -526,11 +526,11 @@
     pwi.filter = "*";
     pwi.notifyNewChanges = true;
     projectsToWatch.add(pwi);
-    requestScopeOperations.setApiUser(user3.getId());
+    requestScopeOperations.setApiUser(user3.id());
     gApi.accounts().self().setWatchedProjects(projectsToWatch);
 
     TestAccount user2 = accountCreator.user2();
-    String pushSpec = "refs/for/master%reviewer=" + user.email + ",cc=" + user2.email;
+    String pushSpec = "refs/for/master%reviewer=" + user.email() + ",cc=" + user2.email();
 
     sender.clear();
     PushOneCommit.Result r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE);
@@ -548,43 +548,44 @@
     r.assertOkStatus();
     assertThat(sender.getMessages()).hasSize(1);
     Message m = sender.getMessages().get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress);
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
 
     sender.clear();
     r = pushTo(pushSpec + ",notify=" + NotifyHandling.ALL);
     r.assertOkStatus();
     assertThat(sender.getMessages()).hasSize(1);
     m = sender.getMessages().get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress, user2.emailAddress, user3.emailAddress);
+    assertThat(m.rcpt())
+        .containsExactly(user.getEmailAddress(), user2.getEmailAddress(), user3.getEmailAddress());
 
     sender.clear();
-    r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-to=" + user3.email);
+    r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-to=" + user3.email());
     r.assertOkStatus();
     assertNotifyTo(user3);
 
     sender.clear();
-    r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-cc=" + user3.email);
+    r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-cc=" + user3.email());
     r.assertOkStatus();
     assertNotifyCc(user3);
 
     sender.clear();
-    r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-bcc=" + user3.email);
+    r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-bcc=" + user3.email());
     r.assertOkStatus();
     assertNotifyBcc(user3);
 
     // request that sender gets notified as TO, CC and BCC, email should be sent
     // even if the sender is the only recipient
     sender.clear();
-    pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-to=" + admin.email);
+    pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-to=" + admin.email());
     assertNotifyTo(admin);
 
     sender.clear();
-    r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-cc=" + admin.email);
+    r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-cc=" + admin.email());
     r.assertOkStatus();
     assertNotifyCc(admin);
 
     sender.clear();
-    r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-bcc=" + admin.email);
+    r = pushTo(pushSpec + ",notify=" + NotifyHandling.NONE + ",notify-bcc=" + admin.email());
     r.assertOkStatus();
     assertNotifyBcc(admin);
   }
@@ -593,7 +594,7 @@
   public void pushForMasterWithCc() throws Exception {
     // cc one user
     String topic = "my/topic";
-    PushOneCommit.Result r = pushTo("refs/for/master/" + topic + "%cc=" + user.email);
+    PushOneCommit.Result r = pushTo("refs/for/master/" + topic + "%cc=" + user.email());
     r.assertOkStatus();
     r.assertChange(Change.Status.NEW, topic, ImmutableList.of(), ImmutableList.of(user));
 
@@ -603,11 +604,11 @@
             "refs/for/master/"
                 + topic
                 + "%cc="
-                + admin.email
+                + admin.email()
                 + ",cc="
-                + user.email
+                + user.email()
                 + ",cc="
-                + accountCreator.user2().email);
+                + accountCreator.user2().email());
     r.assertOkStatus();
     // Check that admin isn't CC'd as they own the change
     r.assertChange(
@@ -623,11 +624,11 @@
             "refs/for/master/"
                 + topic
                 + "%cc="
-                + admin.email
+                + admin.email()
                 + ",cc="
                 + nonExistingEmail
                 + ",cc="
-                + user.email);
+                + user.email());
     r.assertErrorStatus(nonExistingEmail + " does not identify a registered user or group");
   }
 
@@ -659,7 +660,7 @@
     String group = name("group");
     GroupInput gin = new GroupInput();
     gin.name = group;
-    gin.members = ImmutableList.of(user.username, user2.username);
+    gin.members = ImmutableList.of(user.username(), user2.username());
     gin.visibleToAll = true; // TODO(dborowitz): Shouldn't be necessary; see ReviewerAdder.
     gApi.groups().create(gin);
 
@@ -672,7 +673,7 @@
   public void pushForMasterWithReviewer() throws Exception {
     // add one reviewer
     String topic = "my/topic";
-    PushOneCommit.Result r = pushTo("refs/for/master/" + topic + "%r=" + user.email);
+    PushOneCommit.Result r = pushTo("refs/for/master/" + topic + "%r=" + user.email());
     r.assertOkStatus();
     r.assertChange(Change.Status.NEW, topic, user);
 
@@ -684,11 +685,11 @@
             "refs/for/master/"
                 + topic
                 + "%r="
-                + admin.email
+                + admin.email()
                 + ",r="
-                + user.email
+                + user.email()
                 + ",r="
-                + user2.email);
+                + user2.email());
     r.assertOkStatus();
     // admin is the owner of the change and should not appear as reviewer
     r.assertChange(Change.Status.NEW, topic, user, user2);
@@ -700,11 +701,11 @@
             "refs/for/master/"
                 + topic
                 + "%r="
-                + admin.email
+                + admin.email()
                 + ",r="
                 + nonExistingEmail
                 + ",r="
-                + user.email);
+                + user.email());
     r.assertErrorStatus(nonExistingEmail + " does not identify a registered user or group");
   }
 
@@ -737,7 +738,7 @@
     String group = name("group");
     GroupInput gin = new GroupInput();
     gin.name = group;
-    gin.members = ImmutableList.of(user.username, user2.username);
+    gin.members = ImmutableList.of(user.username(), user2.username());
     gin.visibleToAll = true; // TODO(dborowitz): Shouldn't be necessary; see ReviewerAdder.
     gApi.groups().create(gin);
 
@@ -835,9 +836,9 @@
   public void pushWorkInProgressChangeWhenNotOwner() throws Exception {
     TestRepository<?> userRepo = cloneProject(project, user);
     PushOneCommit.Result r =
-        pushFactory.create(user.getIdent(), userRepo).to("refs/for/master%wip");
+        pushFactory.create(user.newIdent(), userRepo).to("refs/for/master%wip");
     r.assertOkStatus();
-    assertThat(r.getChange().change().getOwner()).isEqualTo(user.id);
+    assertThat(r.getChange().change().getOwner()).isEqualTo(user.id());
     assertThat(r.getChange().change().isWorkInProgress()).isTrue();
 
     // Admin user trying to move from WIP to ready should succeed.
@@ -852,7 +853,7 @@
     assertThat(r.getChange().change().isWorkInProgress()).isTrue();
 
     // Push as change owner to move change from WIP to ready.
-    r = pushFactory.create(user.getIdent(), userRepo).to("refs/for/master%ready");
+    r = pushFactory.create(user.newIdent(), userRepo).to("refs/for/master%ready");
     r.assertOkStatus();
     assertThat(r.getChange().change().isWorkInProgress()).isFalse();
 
@@ -933,7 +934,7 @@
     enableCreateNewChangeForAllNotInTarget();
 
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
+        pushFactory.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
     // %2C is comma; the value below tests that percent decoding happens after splitting.
     // All three ways of representing space ("%20", "+", and "_" are also exercised.
     PushOneCommit.Result r = push.to("refs/for/master/%m=my_test%20+_message%2Cm=");
@@ -941,7 +942,7 @@
 
     push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             "b.txt",
@@ -1018,7 +1019,7 @@
 
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             "b.txt",
@@ -1039,7 +1040,7 @@
 
     push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             "c.txt",
@@ -1057,7 +1058,7 @@
 
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             "b.txt",
@@ -1085,8 +1086,8 @@
     // Create a commit with different forged author and committer.
     RevCommit c =
         commitBuilder()
-            .author(user.getIdent())
-            .committer(user2.getIdent())
+            .author(user.newIdent())
+            .committer(user2.newIdent())
             .add(PushOneCommit.FILE_NAME, PushOneCommit.FILE_CONTENT)
             .message(PushOneCommit.SUBJECT)
             .create();
@@ -1094,13 +1095,13 @@
     pushHead(testRepo, "refs/for/master");
 
     String changeId = GitUtil.getChangeId(testRepo, c).get();
-    assertThat(getOwnerEmail(changeId)).isEqualTo(admin.email);
+    assertThat(getOwnerEmail(changeId)).isEqualTo(admin.email());
     assertThat(getReviewerEmails(changeId, ReviewerState.REVIEWER))
-        .containsExactly(user.email, user2.email);
+        .containsExactly(user.email(), user2.email());
 
     assertThat(sender.getMessages()).hasSize(1);
     assertThat(sender.getMessages().get(0).rcpt())
-        .containsExactly(user.emailAddress, user2.emailAddress);
+        .containsExactly(user.getEmailAddress(), user2.getEmailAddress());
   }
 
   @Test
@@ -1109,23 +1110,23 @@
     // First patch set has author and committer matching change owner.
     PushOneCommit.Result r = pushTo("refs/for/master");
 
-    assertThat(getOwnerEmail(r.getChangeId())).isEqualTo(admin.email);
+    assertThat(getOwnerEmail(r.getChangeId())).isEqualTo(admin.email());
     assertThat(getReviewerEmails(r.getChangeId(), ReviewerState.REVIEWER)).isEmpty();
 
     amendBuilder()
-        .author(user.getIdent())
-        .committer(user2.getIdent())
+        .author(user.newIdent())
+        .committer(user2.newIdent())
         .add(PushOneCommit.FILE_NAME, PushOneCommit.FILE_CONTENT + "2")
         .create();
     pushHead(testRepo, "refs/for/master");
 
-    assertThat(getOwnerEmail(r.getChangeId())).isEqualTo(admin.email);
+    assertThat(getOwnerEmail(r.getChangeId())).isEqualTo(admin.email());
     assertThat(getReviewerEmails(r.getChangeId(), ReviewerState.REVIEWER))
-        .containsExactly(user.email, user2.email);
+        .containsExactly(user.email(), user2.email());
 
     assertThat(sender.getMessages()).hasSize(1);
     assertThat(sender.getMessages().get(0).rcpt())
-        .containsExactly(user.emailAddress, user2.emailAddress);
+        .containsExactly(user.getEmailAddress(), user2.getEmailAddress());
   }
 
   /**
@@ -1143,8 +1144,8 @@
     // Create a commit with "User" as author and committer
     RevCommit c =
         commitBuilder()
-            .author(user.getIdent())
-            .committer(user.getIdent())
+            .author(user.newIdent())
+            .committer(user.newIdent())
             .add(PushOneCommit.FILE_NAME, PushOneCommit.FILE_CONTENT)
             .message(PushOneCommit.SUBJECT)
             .create();
@@ -1163,11 +1164,11 @@
         get(GitUtil.getChangeId(testRepo, c).get(), DETAILED_LABELS, MESSAGES, DETAILED_ACCOUNTS);
     LabelInfo cr = ci.labels.get("Code-Review");
     assertThat(cr.all).hasSize(2);
-    int indexAdmin = admin.fullName.equals(cr.all.get(0).name) ? 0 : 1;
+    int indexAdmin = admin.fullName().equals(cr.all.get(0).name) ? 0 : 1;
     int indexUser = indexAdmin == 0 ? 1 : 0;
-    assertThat(cr.all.get(indexAdmin).name).isEqualTo(admin.fullName);
+    assertThat(cr.all.get(indexAdmin).name).isEqualTo(admin.fullName());
     assertThat(cr.all.get(indexAdmin).value.intValue()).isEqualTo(1);
-    assertThat(cr.all.get(indexUser).name).isEqualTo(user.fullName);
+    assertThat(cr.all.get(indexUser).name).isEqualTo(user.fullName());
     assertThat(cr.all.get(indexUser).value.intValue()).isEqualTo(0);
     assertThat(Iterables.getLast(ci.messages).message)
         .isEqualTo("Uploaded patch set 1: Code-Review+1.");
@@ -1189,8 +1190,8 @@
 
     RevCommit c =
         commitBuilder()
-            .author(admin.getIdent())
-            .committer(admin.getIdent())
+            .author(admin.newIdent())
+            .committer(admin.newIdent())
             .add(PushOneCommit.FILE_NAME, PushOneCommit.FILE_CONTENT)
             .message(PushOneCommit.SUBJECT)
             .create();
@@ -1231,7 +1232,7 @@
     r.assertOkStatus();
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             "b.txt",
@@ -1246,7 +1247,7 @@
     r.assertOkStatus();
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             "b.txt",
@@ -1292,7 +1293,7 @@
     String hashtag2 = "tag2";
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             "b.txt",
@@ -1324,7 +1325,7 @@
     String hashtag4 = "tag4";
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             "b.txt",
@@ -1341,7 +1342,7 @@
   public void pushCommitUsingSignedOffBy() throws Exception {
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+            admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
     PushOneCommit.Result r = push.to("refs/for/master");
     r.assertOkStatus();
 
@@ -1350,10 +1351,10 @@
 
     push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT
-                + String.format("\n\nSigned-off-by: %s <%s>", admin.fullName, admin.email),
+                + String.format("\n\nSigned-off-by: %s <%s>", admin.fullName(), admin.email()),
             "b.txt",
             "anotherContent");
     r = push.to("refs/for/master");
@@ -1361,7 +1362,7 @@
 
     push =
         pushFactory.create(
-            admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+            admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
     r = push.to("refs/for/master");
     r.assertErrorStatus("not Signed-off-by author/committer/uploader in message footer");
   }
@@ -1371,13 +1372,13 @@
     enableCreateNewChangeForAllNotInTarget();
 
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
+        pushFactory.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
     PushOneCommit.Result r = push.to("refs/for/master");
     r.assertOkStatus();
 
     push =
         pushFactory.create(
-            admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+            admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
     r = push.to("refs/for/master");
     r.assertOkStatus();
 
@@ -1395,7 +1396,7 @@
 
     // create a change as admin
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
+        pushFactory.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
     PushOneCommit.Result r = push.to("refs/for/master");
     r.assertOkStatus();
     RevCommit commitChange1 = r.getCommit();
@@ -1406,7 +1407,7 @@
     userRepo.reset("change");
     push =
         pushFactory.create(
-            user.getIdent(), userRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+            user.newIdent(), userRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
     r = push.to("refs/for/master");
     r.assertOkStatus();
 
@@ -1424,7 +1425,7 @@
 
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+            admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
 
     PushOneCommit.Result r = push.to("refs/for/master");
     r.assertOkStatus();
@@ -1443,13 +1444,13 @@
     enableCreateNewChangeForAllNotInTarget();
 
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
+        pushFactory.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
     PushOneCommit.Result r = push.to("refs/for/master");
     r.assertOkStatus();
 
     push =
         pushFactory.create(
-            admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+            admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
     r = push.to("refs/for/master");
     r.assertOkStatus();
 
@@ -1464,13 +1465,13 @@
     enableCreateNewChangeForAllNotInTarget();
 
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
+        pushFactory.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
     PushOneCommit.Result r = push.to("refs/for/master");
     r.assertOkStatus();
 
     push =
         pushFactory.create(
-            admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+            admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
     r = push.to("refs/for/master");
     r.assertOkStatus();
 
@@ -1604,7 +1605,7 @@
     r.assertOkStatus();
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT
                 + "\n\n"
@@ -1699,7 +1700,7 @@
   @Test
   public void pushCommitWithSameChangeIdAsPredecessorChange() throws Exception {
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
+        pushFactory.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
     PushOneCommit.Result r = push.to("refs/for/master");
     r.assertOkStatus();
     RevCommit commitChange1 = r.getCommit();
@@ -1834,7 +1835,7 @@
   public void forcePushAbandonedChange() throws Exception {
     grant(project, "refs/*", Permission.PUSH, true);
     PushOneCommit push1 =
-        pushFactory.create(admin.getIdent(), testRepo, "change1", "a.txt", "content");
+        pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
     PushOneCommit.Result r = push1.to("refs/for/master");
     r.assertOkStatus();
 
@@ -1884,12 +1885,12 @@
 
   @Test
   public void pushWithEmailInFooter() throws Exception {
-    pushWithReviewerInFooter(user.emailAddress.toString(), user);
+    pushWithReviewerInFooter(user.getEmailAddress().toString(), user);
   }
 
   @Test
   public void pushWithNameInFooter() throws Exception {
-    pushWithReviewerInFooter(user.fullName, user);
+    pushWithReviewerInFooter(user.fullName(), user);
   }
 
   @Test
@@ -1915,7 +1916,7 @@
     r.assertOkStatus();
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             "b.txt",
@@ -2072,7 +2073,7 @@
 
     assertThat(getPublishedComments(r.getChangeId())).isEmpty();
 
-    gApi.changes().id(r.getChangeId()).addReviewer(user.email);
+    gApi.changes().id(r.getChangeId()).addReviewer(user.email());
     sender.clear();
     amendChange(r.getChangeId(), "refs/for/master%publish-comments");
 
@@ -2186,9 +2187,9 @@
 
     assertThat(getPublishedComments(r.getChangeId())).isEmpty();
 
-    GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id.get()).getPreferences();
+    GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id().get()).getPreferences();
     prefs.publishCommentsOnPush = true;
-    gApi.accounts().id(admin.id.get()).setPreferences(prefs);
+    gApi.accounts().id(admin.id().get()).setPreferences(prefs);
 
     r = amendChange(r.getChangeId());
     assertThat(getPublishedComments(r.getChangeId()).stream().map(c -> c.message))
@@ -2200,9 +2201,9 @@
     PushOneCommit.Result r = createChange();
     addDraft(r.getChangeId(), r.getCommit().name(), newDraft(FILE_NAME, 1, "comment1"));
 
-    GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id.get()).getPreferences();
+    GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id().get()).getPreferences();
     prefs.publishCommentsOnPush = true;
-    gApi.accounts().id(admin.id.get()).setPreferences(prefs);
+    gApi.accounts().id(admin.id().get()).setPreferences(prefs);
 
     r = amendChange(r.getChangeId(), "refs/for/master%no-publish-comments");
 
@@ -2397,7 +2398,7 @@
     PushResult pr = pushHead(testRepo, r, false);
     assertPushOk(pr, r);
 
-    RevCommit amended = testRepo.amend(c).author(user.getIdent()).create();
+    RevCommit amended = testRepo.amend(c).author(user.newIdent()).create();
     testRepo.reset(amended);
 
     pr = pushHead(testRepo, r, false);
@@ -2473,7 +2474,7 @@
 
     PushOneCommit.Result r3 =
         pushFactory
-            .create(admin.getIdent(), testRepo, "another commit", "b.txt", "bbb")
+            .create(admin.newIdent(), testRepo, "another commit", "b.txt", "bbb")
             .to("refs/for/master");
     Change.Id id3 = r3.getChange().getId();
     r3.assertOkStatus();
@@ -2532,7 +2533,7 @@
     assertThat(ci.reviewers).isNotNull();
     assertThat(ci.reviewers.keySet()).containsExactly(ReviewerState.REVIEWER);
     assertThat(ci.reviewers.get(ReviewerState.REVIEWER).iterator().next().email)
-        .isEqualTo(reviewer.email);
+        .isEqualTo(reviewer.email());
   }
 
   private void pushWithReviewerInFooter(String nameEmail, TestAccount expectedReviewer)
@@ -2546,9 +2547,9 @@
       ChangeData cd = byCommit(c);
       String name = "reviewers for " + (i + 1);
       if (expectedReviewer != null) {
-        assertThat(cd.reviewers().all()).named(name).containsExactly(expectedReviewer.getId());
+        assertThat(cd.reviewers().all()).named(name).containsExactly(expectedReviewer.id());
         // Remove reviewer from PS1 so we can test adding this same reviewer on PS2 below.
-        gApi.changes().id(cd.getId().get()).reviewer(expectedReviewer.getId().toString()).remove();
+        gApi.changes().id(cd.getId().get()).reviewer(expectedReviewer.id().toString()).remove();
       }
       assertThat(byCommit(c).reviewers().all()).named(name).isEmpty();
     }
@@ -2559,7 +2560,7 @@
       ChangeData cd = byCommit(c);
       String name = "reviewers for " + (i + 1);
       if (expectedReviewer != null) {
-        assertThat(cd.reviewers().all()).named(name).containsExactly(expectedReviewer.getId());
+        assertThat(cd.reviewers().all()).named(name).containsExactly(expectedReviewer.id());
       } else {
         assertThat(byCommit(c).reviewers().all()).named(name).isEmpty();
       }
diff --git a/javatests/com/google/gerrit/acceptance/git/ForcePushIT.java b/javatests/com/google/gerrit/acceptance/git/ForcePushIT.java
index 652a836..eb4845e 100644
--- a/javatests/com/google/gerrit/acceptance/git/ForcePushIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/ForcePushIT.java
@@ -38,7 +38,7 @@
   public void forcePushNotAllowed() throws Exception {
     ObjectId initial = repo().exactRef(HEAD).getLeaf().getObjectId();
     PushOneCommit push1 =
-        pushFactory.create(admin.getIdent(), testRepo, "change1", "a.txt", "content");
+        pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
     PushOneCommit.Result r1 = push1.to("refs/heads/master");
     r1.assertOkStatus();
 
@@ -48,7 +48,7 @@
     assertThat(ru.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
 
     PushOneCommit push2 =
-        pushFactory.create(admin.getIdent(), testRepo, "change2", "b.txt", "content");
+        pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content");
     push2.setForce(true);
     PushOneCommit.Result r2 = push2.to("refs/heads/master");
     r2.assertErrorStatus("not permitted: force update");
@@ -59,7 +59,7 @@
     ObjectId initial = repo().exactRef(HEAD).getLeaf().getObjectId();
     grant(project, "refs/*", Permission.PUSH, true);
     PushOneCommit push1 =
-        pushFactory.create(admin.getIdent(), testRepo, "change1", "a.txt", "content");
+        pushFactory.create(admin.newIdent(), testRepo, "change1", "a.txt", "content");
     PushOneCommit.Result r1 = push1.to("refs/heads/master");
     r1.assertOkStatus();
 
@@ -69,7 +69,7 @@
     assertThat(ru.forceUpdate()).isEqualTo(RefUpdate.Result.FORCED);
 
     PushOneCommit push2 =
-        pushFactory.create(admin.getIdent(), testRepo, "change2", "b.txt", "content");
+        pushFactory.create(admin.newIdent(), testRepo, "change2", "b.txt", "content");
     push2.setForce(true);
     PushOneCommit.Result r2 = push2.to("refs/heads/master");
     r2.assertOkStatus();
diff --git a/javatests/com/google/gerrit/acceptance/git/HttpPushForReviewIT.java b/javatests/com/google/gerrit/acceptance/git/HttpPushForReviewIT.java
index 6309e79..35260d0 100644
--- a/javatests/com/google/gerrit/acceptance/git/HttpPushForReviewIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/HttpPushForReviewIT.java
@@ -36,7 +36,7 @@
   @Before
   public void selectHttpUrl() throws Exception {
     CredentialsProvider.setDefault(
-        new UsernamePasswordCredentialsProvider(admin.username, admin.httpPassword));
+        new UsernamePasswordCredentialsProvider(admin.username(), admin.httpPassword()));
     selectProtocol(Protocol.HTTP);
     // Don't clear audit events here, since we can't guarantee all test setup has run yet.
   }
@@ -55,13 +55,13 @@
     assertThat(auditEvents).hasSize(2);
 
     HttpAuditEvent lsRemote = auditEvents.get(0);
-    assertThat(lsRemote.who.getAccountId()).isEqualTo(admin.id);
+    assertThat(lsRemote.who.getAccountId()).isEqualTo(admin.id());
     assertThat(lsRemote.what).endsWith("/info/refs?service=git-receive-pack");
     assertThat(lsRemote.params).containsExactly("service", "git-receive-pack");
     assertThat(lsRemote.httpStatus).isEqualTo(HttpServletResponse.SC_OK);
 
     HttpAuditEvent receivePack = auditEvents.get(1);
-    assertThat(receivePack.who.getAccountId()).isEqualTo(admin.id);
+    assertThat(receivePack.who.getAccountId()).isEqualTo(admin.id());
     assertThat(receivePack.what).endsWith("/git-receive-pack");
     assertThat(receivePack.params).isEmpty();
     assertThat(receivePack.httpStatus).isEqualTo(HttpServletResponse.SC_OK);
diff --git a/javatests/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java b/javatests/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java
index eed506c..6516b32 100644
--- a/javatests/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/ImplicitMergeCheckIT.java
@@ -91,7 +91,7 @@
 
   private PushOneCommit.Result push(String ref, String subject, String fileName, String content)
       throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo, subject, fileName, content);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content);
     return push.to(ref);
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java b/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
index dd0a5cb..0ef3473 100644
--- a/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
@@ -268,14 +268,14 @@
   @Test
   public void addPatchSetDenied() throws Exception {
     grant(project, "refs/for/refs/heads/*", Permission.PUSH, false, REGISTERED_USERS);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     ChangeInput ci = new ChangeInput();
     ci.project = project.get();
     ci.branch = "master";
     ci.subject = "A change";
     Change.Id id = new Change.Id(gApi.changes().create(ci).get()._number);
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     ObjectId ps1Id = forceFetch(new PatchSet.Id(id, 1).toRefName());
     ObjectId ps2Id = testRepo.amend(ps1Id).add("file", "content").create();
     PushResult r = push(ps2Id.name() + ":refs/for/master");
diff --git a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
index ff4a423..02549a7 100644
--- a/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
@@ -142,25 +142,25 @@
     // visible.
     allow("refs/for/refs/heads/*", Permission.SUBMIT, admins);
     PushOneCommit.Result mr =
-        pushFactory.create(admin.getIdent(), testRepo).to("refs/for/master%submit");
+        pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%submit");
     mr.assertOkStatus();
     cd1 = mr.getChange();
     psRef1 = cd1.currentPatchSet().getId().toRefName();
     metaRef1 = RefNames.changeMetaRef(cd1.getId());
     PushOneCommit.Result br =
-        pushFactory.create(admin.getIdent(), testRepo).to("refs/for/branch%submit");
+        pushFactory.create(admin.newIdent(), testRepo).to("refs/for/branch%submit");
     br.assertOkStatus();
     cd2 = br.getChange();
     psRef2 = cd2.currentPatchSet().getId().toRefName();
     metaRef2 = RefNames.changeMetaRef(cd2.getId());
 
     // Second 2 changes are unmerged.
-    mr = pushFactory.create(admin.getIdent(), testRepo).to("refs/for/master");
+    mr = pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master");
     mr.assertOkStatus();
     cd3 = mr.getChange();
     psRef3 = cd3.currentPatchSet().getId().toRefName();
     metaRef3 = RefNames.changeMetaRef(cd3.getId());
-    br = pushFactory.create(admin.getIdent(), testRepo).to("refs/for/branch");
+    br = pushFactory.create(admin.newIdent(), testRepo).to("refs/for/branch");
     br.assertOkStatus();
     cd4 = br.getChange();
     psRef4 = cd4.currentPatchSet().getId().toRefName();
@@ -190,7 +190,7 @@
       u.save();
     }
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertUploadPackRefs(
         "HEAD",
         psRef1,
@@ -234,7 +234,7 @@
     allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
     deny("refs/heads/branch", Permission.READ, REGISTERED_USERS);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertUploadPackRefs(
         "HEAD", psRef1, metaRef1, psRef3, metaRef3, "refs/heads/master", "refs/tags/master-tag");
   }
@@ -244,7 +244,7 @@
     deny("refs/heads/master", Permission.READ, REGISTERED_USERS);
     allow("refs/heads/branch", Permission.READ, REGISTERED_USERS);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertUploadPackRefs(
         psRef2,
         metaRef2,
@@ -262,11 +262,11 @@
     allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
 
     // Admin's edit is not visible.
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(cd3.getId().get()).edit().create();
 
     // User's edit is visible.
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(cd3.getId().get()).edit().create();
 
     assertUploadPackRefs(
@@ -286,14 +286,14 @@
     allow("refs/*", Permission.VIEW_PRIVATE_CHANGES, REGISTERED_USERS);
 
     // Admin's edit on change3 is visible.
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(cd3.getId().get()).edit().create();
 
     // Admin's edit on change4 is not visible since user cannot see the change.
     gApi.changes().id(cd4.getId().get()).edit().create();
 
     // User's edit is visible.
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(cd3.getId().get()).edit().create();
 
     assertUploadPackRefs(
@@ -314,9 +314,9 @@
     deny("refs/heads/master", Permission.READ, REGISTERED_USERS);
     allow("refs/heads/branch", Permission.READ, REGISTERED_USERS);
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(cd3.getId().get()).edit().create();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     assertUploadPackRefs(
         // Change 1 is visible due to accessDatabase capability, even though
@@ -351,7 +351,7 @@
   private void uploadPackNoSearchingChangeCacheImpl() throws Exception {
     allow("refs/heads/*", Permission.READ, REGISTERED_USERS);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertRefs(
         project,
         user,
@@ -387,7 +387,7 @@
     gApi.changes().id(cd4.getId().id).delete();
     gApi.projects().name(project.get()).branch("refs/heads/branch").delete();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertUploadPackRefs(
         "HEAD",
         "refs/meta/config",
@@ -421,7 +421,7 @@
   public void receivePackRespectsVisibilityOfOpenChanges() throws Exception {
     allow("refs/heads/master", Permission.READ, REGISTERED_USERS);
     deny("refs/heads/branch", Permission.READ, REGISTERED_USERS);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     assertThat(getReceivePackRefs().additionalHaves()).containsExactly(obj(cd3, 1));
   }
@@ -447,7 +447,7 @@
 
       PersonIdent committer = serverIdent.get();
       PersonIdent author =
-          noteUtil.newIdent(getAccount(admin.getId()), committer.getWhen(), committer);
+          noteUtil.newIdent(getAccount(admin.id()), committer.getWhen(), committer);
       tr.branch(RefNames.changeMetaRef(cd3.getId()))
           .commit()
           .author(author)
@@ -487,7 +487,7 @@
     TestRepository<?> userTestRepository = cloneProject(allUsers, user);
     try (Git git = userTestRepository.git()) {
       assertThat(getUserRefs(git))
-          .containsExactly(RefNames.REFS_USERS_SELF, RefNames.refsUsers(user.id));
+          .containsExactly(RefNames.REFS_USERS_SELF, RefNames.refsUsers(user.id()));
     }
   }
 
@@ -498,7 +498,9 @@
     try (Git git = userTestRepository.git()) {
       assertThat(getUserRefs(git))
           .containsExactly(
-              RefNames.REFS_USERS_SELF, RefNames.refsUsers(user.id), RefNames.refsUsers(admin.id));
+              RefNames.REFS_USERS_SELF,
+              RefNames.refsUsers(user.id()),
+              RefNames.refsUsers(admin.id()));
     }
   }
 
@@ -614,13 +616,13 @@
     allow(project, "refs/*", Permission.READ, REGISTERED_USERS);
     allow(allUsersName, "refs/*", Permission.READ, REGISTERED_USERS);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     DraftInput draftInput = new DraftInput();
     draftInput.line = 1;
     draftInput.message = "nit: trailing whitespace";
     draftInput.path = Patch.COMMIT_MSG;
     gApi.changes().id(cd3.getId().get()).current().createDraft(draftInput);
-    String draftCommentRef = RefNames.refsDraftComments(cd3.getId(), user.id);
+    String draftCommentRef = RefNames.refsDraftComments(cd3.getId(), user.id());
 
     // user can see the draft comment ref of the own draft comment
     assertThat(lsRemote(allUsersName, user)).contains(draftCommentRef);
@@ -634,9 +636,9 @@
     allow(project, "refs/*", Permission.READ, REGISTERED_USERS);
     allow(allUsersName, "refs/*", Permission.READ, REGISTERED_USERS);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.accounts().self().starChange(cd3.getId().toString());
-    String starredChangesRef = RefNames.refsStarredChanges(cd3.getId(), user.id);
+    String starredChangesRef = RefNames.refsStarredChanges(cd3.getId(), user.id());
 
     // user can see the starred changes ref of the own star
     assertThat(lsRemote(allUsersName, user)).contains(starredChangesRef);
@@ -654,15 +656,15 @@
     allUsersRepo.reset("userRef");
     PushOneCommit.Result mr =
         pushFactory
-            .create(admin.getIdent(), allUsersRepo)
+            .create(admin.newIdent(), allUsersRepo)
             .to("refs/for/" + RefNames.REFS_USERS_SELF);
     mr.assertOkStatus();
 
     List<String> expectedNonMetaRefs =
         ImmutableList.of(
             RefNames.REFS_USERS_SELF,
-            RefNames.refsUsers(admin.id),
-            RefNames.refsUsers(user.id),
+            RefNames.refsUsers(admin.id()),
+            RefNames.refsUsers(user.id()),
             RefNames.REFS_EXTERNAL_IDS,
             RefNames.REFS_GROUPNAMES,
             RefNames.refsGroups(admins),
@@ -789,7 +791,7 @@
     groupInput.name = name(name);
     groupInput.ownerId = ownerGroup != null ? ownerGroup.get() : null;
     groupInput.members =
-        Arrays.stream(members).map(m -> String.valueOf(m.id.get())).collect(toList());
+        Arrays.stream(members).map(m -> String.valueOf(m.id().get())).collect(toList());
     return new AccountGroup.UUID(gApi.groups().create(groupInput).get().id);
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/git/SubmitOnPushIT.java b/javatests/com/google/gerrit/acceptance/git/SubmitOnPushIT.java
index f583274..759a99a 100644
--- a/javatests/com/google/gerrit/acceptance/git/SubmitOnPushIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/SubmitOnPushIT.java
@@ -218,7 +218,7 @@
 
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             PushOneCommit.SUBJECT,
             "b.txt",
@@ -324,7 +324,7 @@
     PatchSetApproval a = getSubmitter(patchSetId);
     assertThat(a.isLegacySubmit()).isTrue();
     assertThat(a.getValue()).isEqualTo((short) 1);
-    assertThat(a.getAccountId()).isEqualTo(admin.id);
+    assertThat(a.getAccountId()).isEqualTo(admin.id());
   }
 
   private void assertCommit(Project.NameKey project, String branch) throws Exception {
@@ -332,8 +332,8 @@
         RevWalk rw = new RevWalk(r)) {
       RevCommit c = rw.parseCommit(r.exactRef(branch).getObjectId());
       assertThat(c.getShortMessage()).isEqualTo(PushOneCommit.SUBJECT);
-      assertThat(c.getAuthorIdent().getEmailAddress()).isEqualTo(admin.email);
-      assertThat(c.getCommitterIdent().getEmailAddress()).isEqualTo(admin.email);
+      assertThat(c.getAuthorIdent().getEmailAddress()).isEqualTo(admin.email());
+      assertThat(c.getCommitterIdent().getEmailAddress()).isEqualTo(admin.email());
     }
   }
 
@@ -343,7 +343,7 @@
       RevCommit c = rw.parseCommit(r.exactRef(branch).getObjectId());
       assertThat(c.getParentCount()).isEqualTo(2);
       assertThat(c.getShortMessage()).isEqualTo("Merge \"" + subject + "\"");
-      assertThat(c.getAuthorIdent().getEmailAddress()).isEqualTo(admin.email);
+      assertThat(c.getAuthorIdent().getEmailAddress()).isEqualTo(admin.email());
       assertThat(c.getCommitterIdent().getEmailAddress())
           .isEqualTo(serverIdent.get().getEmailAddress());
     }
@@ -351,7 +351,7 @@
 
   private PushOneCommit.Result push(String ref, String subject, String fileName, String content)
       throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo, subject, fileName, content);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content);
     return push.to(ref);
   }
 
@@ -359,7 +359,7 @@
       String ref, String subject, String fileName, String content, String changeId)
       throws Exception {
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), testRepo, subject, fileName, content, changeId);
+        pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content, changeId);
     return push.to(ref);
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java
index e422c95..a04fe3c 100644
--- a/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/SubmoduleSubscriptionsIT.java
@@ -495,8 +495,8 @@
       // Expect that the author name/email is preserved for the superRepo commit, but a new author
       // timestamp is used.
       PersonIdent authorIdent = getAuthor(superRepo, "master");
-      assertThat(authorIdent.getName()).isEqualTo(admin.fullName);
-      assertThat(authorIdent.getEmailAddress()).isEqualTo(admin.email);
+      assertThat(authorIdent.getName()).isEqualTo(admin.fullName());
+      assertThat(authorIdent.getEmailAddress()).isEqualTo(admin.email());
       assertThat(authorIdent.getWhen())
           .isGreaterThan(pushResult.getCommit().getAuthorIdent().getWhen());
     } finally {
@@ -541,8 +541,8 @@
       // Expect that the author name/email is preserved for the superRepo commit, but a new author
       // timestamp is used.
       PersonIdent authorIdent = getAuthor(superRepo, "master");
-      assertThat(authorIdent.getName()).isEqualTo(admin.fullName);
-      assertThat(authorIdent.getEmailAddress()).isEqualTo(admin.email);
+      assertThat(authorIdent.getName()).isEqualTo(admin.fullName());
+      assertThat(authorIdent.getEmailAddress()).isEqualTo(admin.email());
       assertThat(authorIdent.getWhen())
           .isGreaterThan(pushResult1.getCommit().getAuthorIdent().getWhen());
       assertThat(authorIdent.getWhen())
@@ -581,7 +581,7 @@
 
       // Create change as user.
       PushOneCommit push =
-          pushFactory.create(user.getIdent(), repo2, "Change 2", "b.txt", "other content");
+          pushFactory.create(user.newIdent(), repo2, "Change 2", "b.txt", "other content");
       PushOneCommit.Result pushResult2 = push.to("refs/for/master/" + name(topic));
       approve(pushResult2.getChangeId());
 
diff --git a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
index 0a5510a..b30dc41 100644
--- a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
@@ -171,7 +171,7 @@
 
   @Test
   public void pushWithoutTrace() throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result r = push.to("refs/heads/master");
     r.assertOkStatus();
     assertThat(commitValidationListener.traceId).isNull();
@@ -180,7 +180,7 @@
 
   @Test
   public void pushWithTrace() throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     push.setPushOptions(ImmutableList.of("trace"));
     PushOneCommit.Result r = push.to("refs/heads/master");
     r.assertOkStatus();
@@ -190,7 +190,7 @@
 
   @Test
   public void pushWithTraceAndProvidedTraceId() throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     push.setPushOptions(ImmutableList.of("trace=issue/123"));
     PushOneCommit.Result r = push.to("refs/heads/master");
     r.assertOkStatus();
@@ -200,7 +200,7 @@
 
   @Test
   public void pushForReviewWithoutTrace() throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result r = push.to("refs/for/master");
     r.assertOkStatus();
     assertThat(commitValidationListener.traceId).isNull();
@@ -209,7 +209,7 @@
 
   @Test
   public void pushForReviewWithTrace() throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     push.setPushOptions(ImmutableList.of("trace"));
     PushOneCommit.Result r = push.to("refs/for/master");
     r.assertOkStatus();
@@ -219,7 +219,7 @@
 
   @Test
   public void pushForReviewWithTraceAndProvidedTraceId() throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     push.setPushOptions(ImmutableList.of("trace=issue/123"));
     PushOneCommit.Result r = push.to("refs/for/master");
     r.assertOkStatus();
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java b/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
index 2baaef8..5e652c0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/AccountAssert.java
@@ -25,9 +25,9 @@
 public class AccountAssert {
 
   public static void assertAccountInfo(TestAccount a, AccountInfo ai) {
-    assertThat(a.id.get()).isEqualTo(ai._accountId);
-    assertThat(a.fullName).isEqualTo(ai.name);
-    assertThat(a.email).isEqualTo(ai.email);
+    assertThat(a.id().get()).isEqualTo(ai._accountId);
+    assertThat(a.fullName()).isEqualTo(ai.name);
+    assertThat(a.email()).isEqualTo(ai.email);
   }
 
   public static void assertAccountInfos(List<TestAccount> expected, List<AccountInfo> actual) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/BUILD b/javatests/com/google/gerrit/acceptance/rest/account/BUILD
index 433b854..3b46414 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/BUILD
+++ b/javatests/com/google/gerrit/acceptance/rest/account/BUILD
@@ -18,7 +18,6 @@
     deps = [
         "//java/com/google/gerrit/acceptance:lib",
         "//java/com/google/gerrit/reviewdb:server",
-        "//lib:gwtorm",
         "//lib:junit",
     ],
 )
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java b/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
index 0bdb4e4..84f218d 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/EmailIT.java
@@ -134,11 +134,11 @@
         .get()
         .update(
             "Add External ID",
-            admin.id,
+            admin.id(),
             u ->
                 u.addExternalId(
                     ExternalId.createWithEmail(
-                        ExternalId.SCHEME_EXTERNAL, "foo", admin.id, email)));
+                        ExternalId.SCHEME_EXTERNAL, "foo", admin.id(), email)));
     assertThat(gApi.accounts().self().get().email).isNotEqualTo(email);
 
     requestScopeOperations.resetCurrentApiUser();
@@ -157,8 +157,8 @@
   @Test
   public void setPreferredEmailToEmailOfOtherAccount() throws Exception {
     exception.expect(ResourceNotFoundException.class);
-    exception.expectMessage("Not found: " + user.email);
-    gApi.accounts().self().email(user.email).setPreferred();
+    exception.expectMessage("Not found: " + user.email());
+    gApi.accounts().self().email(user.email()).setPreferred();
   }
 
   @Test
@@ -181,12 +181,12 @@
     assertThat(externalIds.get(mailtoExtIdKey)).isEmpty();
     assertThat(gApi.accounts().self().get().email).isNotEqualTo(email);
 
-    Context oldCtx = createContextWithCustomRealm(new RealmWithAdditionalEmails(admin.id, email));
+    Context oldCtx = createContextWithCustomRealm(new RealmWithAdditionalEmails(admin.id(), email));
     try {
       gApi.accounts().self().email(email).setPreferred();
       Optional<ExternalId> mailtoExtId = externalIds.get(mailtoExtIdKey);
       assertThat(mailtoExtId).isPresent();
-      assertThat(mailtoExtId.get().accountId()).isEqualTo(admin.id);
+      assertThat(mailtoExtId.get().accountId()).isEqualTo(admin.id());
       assertThat(gApi.accounts().self().get().email).isEqualTo(email);
     } finally {
       atrScope.set(oldCtx);
@@ -195,15 +195,15 @@
 
   @Test
   public void setPreferredEmailToEmailFromCustomRealmThatBelongsToOtherAccount() throws Exception {
-    ExternalId mailToExtId = ExternalId.createEmail(user.id, user.email);
+    ExternalId mailToExtId = ExternalId.createEmail(user.id(), user.email());
     assertThat(externalIds.get(mailToExtId.key())).isPresent();
 
     Context oldCtx =
-        createContextWithCustomRealm(new RealmWithAdditionalEmails(admin.id, user.email));
+        createContextWithCustomRealm(new RealmWithAdditionalEmails(admin.id(), user.email()));
     try {
       exception.expect(ResourceConflictException.class);
       exception.expectMessage("email in use by another account");
-      gApi.accounts().self().email(user.email).setPreferred();
+      gApi.accounts().self().email(user.email()).setPreferred();
     } finally {
       atrScope.set(oldCtx);
     }
@@ -278,7 +278,7 @@
             enableReverseDnsLookup,
             accountCache,
             groupBackend);
-    return atrScope.set(atrScope.newContext(null, userFactory.create(admin.id)));
+    return atrScope.set(atrScope.newContext(null, userFactory.create(admin.id())));
   }
 
   private class RealmWithAdditionalEmails extends DefaultRealm {
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
index 8e0aa01..8ff80a0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
@@ -36,6 +36,7 @@
 import com.google.gerrit.acceptance.testsuite.request.RequestScopeOperations;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.exceptions.DuplicateKeyException;
 import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo;
 import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo.ConsistencyProblemInfo;
 import com.google.gerrit.extensions.api.config.ConsistencyCheckInput;
@@ -54,7 +55,6 @@
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.meta.MetaDataUpdate;
 import com.google.gson.reflect.TypeToken;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -95,7 +95,7 @@
 
   @Test
   public void getExternalIds() throws Exception {
-    Collection<ExternalId> expectedIds = getAccountState(user.getId()).getExternalIds();
+    Collection<ExternalId> expectedIds = getAccountState(user.id()).getExternalIds();
     List<AccountExternalIdInfo> expectedIdInfos = toExternalIdInfos(expectedIds);
 
     RestResponse response = userRestSession.get("/accounts/self/external.ids");
@@ -111,20 +111,20 @@
 
   @Test
   public void getExternalIdsOfOtherUserNotAllowed() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("access database not permitted");
-    gApi.accounts().id(admin.id.get()).getExternalIds();
+    gApi.accounts().id(admin.id().get()).getExternalIds();
   }
 
   @Test
   public void getExternalIdsOfOtherUserWithAccessDatabase() throws Exception {
     allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
 
-    Collection<ExternalId> expectedIds = getAccountState(admin.getId()).getExternalIds();
+    Collection<ExternalId> expectedIds = getAccountState(admin.id()).getExternalIds();
     List<AccountExternalIdInfo> expectedIdInfos = toExternalIdInfos(expectedIds);
 
-    RestResponse response = userRestSession.get("/accounts/" + admin.id + "/external.ids");
+    RestResponse response = userRestSession.get("/accounts/" + admin.id() + "/external.ids");
     response.assertOK();
 
     List<AccountExternalIdInfo> results =
@@ -137,7 +137,7 @@
 
   @Test
   public void deleteExternalIds() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     List<AccountExternalIdInfo> externalIds = gApi.accounts().self().getExternalIds();
 
     List<String> toDelete = new ArrayList<>();
@@ -164,18 +164,18 @@
   @Test
   public void deleteExternalIdsOfOtherUserNotAllowed() throws Exception {
     List<AccountExternalIdInfo> extIds = gApi.accounts().self().getExternalIds();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("access database not permitted");
     gApi.accounts()
-        .id(admin.id.get())
+        .id(admin.id().get())
         .deleteExternalIds(extIds.stream().map(e -> e.identity).collect(toList()));
   }
 
   @Test
   public void deleteExternalIdOfOtherUserUnderOwnAccount_UnprocessableEntity() throws Exception {
     List<AccountExternalIdInfo> extIds = gApi.accounts().self().getExternalIds();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(UnprocessableEntityException.class);
     exception.expectMessage(String.format("External id %s does not exist", extIds.get(0).identity));
     gApi.accounts()
@@ -201,11 +201,11 @@
 
     assertThat(toDelete).hasSize(1);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     RestResponse response =
-        userRestSession.post("/accounts/" + admin.id + "/external.ids:delete", toDelete);
+        userRestSession.post("/accounts/" + admin.id() + "/external.ids:delete", toDelete);
     response.assertNoContent();
-    List<AccountExternalIdInfo> results = gApi.accounts().id(admin.id.get()).getExternalIds();
+    List<AccountExternalIdInfo> results = gApi.accounts().id(admin.id().get()).getExternalIds();
     // The external ID in WebSession will not be set for tests, resulting that
     // "mailto:user@example.com" can be deleted while "username:user" can't.
     assertThat(results).hasSize(1);
@@ -227,7 +227,7 @@
   @Test
   public void deleteExternalIds_Conflict() throws Exception {
     List<String> toDelete = new ArrayList<>();
-    String externalIdStr = "username:" + user.username;
+    String externalIdStr = "username:" + user.username();
     toDelete.add(externalIdStr);
     RestResponse response = userRestSession.post("/accounts/self/external.ids:delete", toDelete);
     response.assertConflict();
@@ -463,7 +463,7 @@
     insertExtId(
         ExternalId.createWithPassword(
             ExternalId.Key.parse(nextId(scheme, i)),
-            admin.id,
+            admin.id(),
             "admin.other@example.com",
             "secret-password"));
     insertExtId(createExternalIdWithOtherCaseEmail(nextId(scheme, i)));
@@ -563,7 +563,10 @@
 
   private ExternalId createExternalIdWithOtherCaseEmail(String externalId) {
     return ExternalId.createWithPassword(
-        ExternalId.Key.parse(externalId), admin.id, admin.email.toUpperCase(Locale.US), "password");
+        ExternalId.Key.parse(externalId),
+        admin.id(),
+        admin.email().toUpperCase(Locale.US),
+        "password");
   }
 
   private String insertExternalIdWithoutAccountId(Repository repo, RevWalk rw, String externalId)
@@ -572,7 +575,7 @@
         repo,
         rw,
         (ins, noteMap) -> {
-          ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), admin.id);
+          ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), admin.id());
           ObjectId noteId = extId.key().sha1();
           Config c = new Config();
           extId.writeToConfig(c);
@@ -590,7 +593,7 @@
         repo,
         rw,
         (ins, noteMap) -> {
-          ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), admin.id);
+          ExternalId extId = ExternalId.create(ExternalId.Key.parse(externalId), admin.id());
           ObjectId noteId = ExternalId.Key.parse(externalId + "x").sha1();
           Config c = new Config();
           extId.writeToConfig(c);
@@ -640,8 +643,8 @@
       CommitBuilder cb = new CommitBuilder();
       cb.setMessage("Update external IDs");
       cb.setTreeId(noteMap.writeTree(ins));
-      cb.setAuthor(admin.getIdent());
-      cb.setCommitter(admin.getIdent());
+      cb.setAuthor(admin.newIdent());
+      cb.setCommitter(admin.newIdent());
       if (!rev.equals(ObjectId.zeroId())) {
         cb.setParentId(rev);
       } else {
@@ -688,17 +691,18 @@
   }
 
   private ExternalId createExternalIdWithInvalidEmail(String externalId) {
-    return ExternalId.createWithEmail(ExternalId.Key.parse(externalId), admin.id, "invalid-email");
+    return ExternalId.createWithEmail(
+        ExternalId.Key.parse(externalId), admin.id(), "invalid-email");
   }
 
   private ExternalId createExternalIdWithDuplicateEmail(String externalId) {
-    return ExternalId.createWithEmail(ExternalId.Key.parse(externalId), admin.id, admin.email);
+    return ExternalId.createWithEmail(ExternalId.Key.parse(externalId), admin.id(), admin.email());
   }
 
   private ExternalId createExternalIdWithBadPassword(String username) {
     return ExternalId.create(
         ExternalId.Key.create(SCHEME_USERNAME, username),
-        admin.id,
+        admin.id(),
         null,
         "non-hashed-password-is-not-allowed");
   }
@@ -723,29 +727,30 @@
 
   @Test
   public void checkNoReloadAfterUpdate() throws Exception {
-    Set<ExternalId> expectedExtIds = new HashSet<>(externalIds.byAccount(admin.id));
+    Set<ExternalId> expectedExtIds = new HashSet<>(externalIds.byAccount(admin.id()));
     try (AutoCloseable ctx = createFailOnLoadContext()) {
       // insert external ID
-      ExternalId extId = ExternalId.create("foo", "bar", admin.id);
+      ExternalId extId = ExternalId.create("foo", "bar", admin.id());
       insertExtId(extId);
       expectedExtIds.add(extId);
-      assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExtIds);
+      assertThat(externalIds.byAccount(admin.id())).containsExactlyElementsIn(expectedExtIds);
 
       // update external ID
       expectedExtIds.remove(extId);
-      ExternalId extId2 = ExternalId.createWithEmail("foo", "bar", admin.id, "foo.bar@example.com");
+      ExternalId extId2 =
+          ExternalId.createWithEmail("foo", "bar", admin.id(), "foo.bar@example.com");
       accountsUpdateProvider
           .get()
-          .update("Update External ID", admin.id, u -> u.updateExternalId(extId2));
+          .update("Update External ID", admin.id(), u -> u.updateExternalId(extId2));
       expectedExtIds.add(extId2);
-      assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExtIds);
+      assertThat(externalIds.byAccount(admin.id())).containsExactlyElementsIn(expectedExtIds);
 
       // delete external ID
       accountsUpdateProvider
           .get()
-          .update("Delete External ID", admin.id, u -> u.deleteExternalId(extId));
+          .update("Delete External ID", admin.id(), u -> u.deleteExternalId(extId));
       expectedExtIds.remove(extId2);
-      assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExtIds);
+      assertThat(externalIds.byAccount(admin.id())).containsExactlyElementsIn(expectedExtIds);
     }
   }
 
@@ -753,10 +758,10 @@
   public void byAccountFailIfReadingExternalIdsFails() throws Exception {
     try (AutoCloseable ctx = createFailOnLoadContext()) {
       // update external ID branch so that external IDs need to be reloaded
-      insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id));
+      insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id()));
 
       exception.expect(IOException.class);
-      externalIds.byAccount(admin.id);
+      externalIds.byAccount(admin.id());
     }
   }
 
@@ -764,28 +769,28 @@
   public void byEmailFailIfReadingExternalIdsFails() throws Exception {
     try (AutoCloseable ctx = createFailOnLoadContext()) {
       // update external ID branch so that external IDs need to be reloaded
-      insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id));
+      insertExtIdBehindGerritsBack(ExternalId.create("foo", "bar", admin.id()));
 
       exception.expect(IOException.class);
-      externalIds.byEmail(admin.email);
+      externalIds.byEmail(admin.email());
     }
   }
 
   @Test
   public void byAccountUpdateExternalIdsBehindGerritsBack() throws Exception {
-    Set<ExternalId> expectedExternalIds = new HashSet<>(externalIds.byAccount(admin.id));
-    ExternalId newExtId = ExternalId.create("foo", "bar", admin.id);
+    Set<ExternalId> expectedExternalIds = new HashSet<>(externalIds.byAccount(admin.id()));
+    ExternalId newExtId = ExternalId.create("foo", "bar", admin.id());
     insertExtIdBehindGerritsBack(newExtId);
     expectedExternalIds.add(newExtId);
-    assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExternalIds);
+    assertThat(externalIds.byAccount(admin.id())).containsExactlyElementsIn(expectedExternalIds);
   }
 
   @Test
   public void unsetEmail() throws Exception {
-    ExternalId extId = ExternalId.createWithEmail("x", "1", user.id, "x@example.com");
+    ExternalId extId = ExternalId.createWithEmail("x", "1", user.id(), "x@example.com");
     insertExtId(extId);
 
-    ExternalId extIdWithoutEmail = ExternalId.create("x", "1", user.id);
+    ExternalId extIdWithoutEmail = ExternalId.create("x", "1", user.id());
     try (Repository allUsersRepo = repoManager.openRepository(allUsers);
         MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
       ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
@@ -799,10 +804,10 @@
   @Test
   public void unsetHttpPassword() throws Exception {
     ExternalId extId =
-        ExternalId.createWithPassword(ExternalId.Key.create("y", "1"), user.id, null, "secret");
+        ExternalId.createWithPassword(ExternalId.Key.create("y", "1"), user.id(), null, "secret");
     insertExtId(extId);
 
-    ExternalId extIdWithoutPassword = ExternalId.create("y", "1", user.id);
+    ExternalId extIdWithoutPassword = ExternalId.create("y", "1", user.id());
     try (Repository allUsersRepo = repoManager.openRepository(allUsers);
         MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
       ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
@@ -818,22 +823,22 @@
     // Insert external ID for different accounts
     TestAccount user1 = accountCreator.create("user1");
     TestAccount user2 = accountCreator.create("user2");
-    ExternalId extId1 = ExternalId.create("foo", "1", user1.id);
-    ExternalId extId2 = ExternalId.create("foo", "2", user1.id);
-    ExternalId extId3 = ExternalId.create("foo", "3", user2.id);
+    ExternalId extId1 = ExternalId.create("foo", "1", user1.id());
+    ExternalId extId2 = ExternalId.create("foo", "2", user1.id());
+    ExternalId extId3 = ExternalId.create("foo", "3", user2.id());
     try (Repository allUsersRepo = repoManager.openRepository(allUsers);
         MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
       ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
       extIdNotes.insert(ImmutableSet.of(extId1, extId2, extId3));
       RevCommit c = extIdNotes.commit(md);
       assertThat(getFooters(c))
-          .containsExactly("Account: " + user1.getId(), "Account: " + user2.getId())
+          .containsExactly("Account: " + user1.id(), "Account: " + user2.id())
           .inOrder();
     }
 
     // Insert external ID with different emails
-    ExternalId extId4 = ExternalId.createWithEmail("foo", "4", user1.id, "foo4@example.com");
-    ExternalId extId5 = ExternalId.createWithEmail("foo", "5", user2.id, "foo5@example.com");
+    ExternalId extId4 = ExternalId.createWithEmail("foo", "4", user1.id(), "foo4@example.com");
+    ExternalId extId5 = ExternalId.createWithEmail("foo", "5", user2.id(), "foo5@example.com");
     try (Repository allUsersRepo = repoManager.openRepository(allUsers);
         MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
       ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
@@ -841,22 +846,22 @@
       RevCommit c = extIdNotes.commit(md);
       assertThat(getFooters(c))
           .containsExactly(
-              "Account: " + user1.getId(),
-              "Account: " + user2.getId(),
+              "Account: " + user1.id(),
+              "Account: " + user2.id(),
               "Email: foo4@example.com",
               "Email: foo5@example.com")
           .inOrder();
     }
 
     // Update external ID - Add Email
-    ExternalId extId1a = ExternalId.createWithEmail("foo", "1", user1.id, "foo1@example.com");
+    ExternalId extId1a = ExternalId.createWithEmail("foo", "1", user1.id(), "foo1@example.com");
     try (Repository allUsersRepo = repoManager.openRepository(allUsers);
         MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
       ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
       extIdNotes.upsert(extId1a);
       RevCommit c = extIdNotes.commit(md);
       assertThat(getFooters(c))
-          .containsExactly("Account: " + user1.getId(), "Email: foo1@example.com")
+          .containsExactly("Account: " + user1.id(), "Email: foo1@example.com")
           .inOrder();
     }
 
@@ -867,7 +872,7 @@
       extIdNotes.upsert(extId1);
       RevCommit c = extIdNotes.commit(md);
       assertThat(getFooters(c))
-          .containsExactly("Account: " + user1.getId(), "Email: foo1@example.com")
+          .containsExactly("Account: " + user1.id(), "Email: foo1@example.com")
           .inOrder();
     }
 
@@ -879,7 +884,7 @@
       RevCommit c = extIdNotes.commit(md);
       assertThat(getFooters(c))
           .containsExactly(
-              "Account: " + user1.getId(), "Account: " + user2.getId(), "Email: foo5@example.com")
+              "Account: " + user1.id(), "Account: " + user2.id(), "Email: foo5@example.com")
           .inOrder();
     }
 
@@ -889,7 +894,7 @@
       ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
       extIdNotes.delete(extId2.accountId(), extId2.key());
       RevCommit c = extIdNotes.commit(md);
-      assertThat(getFooters(c)).containsExactly("Account: " + user1.getId()).inOrder();
+      assertThat(getFooters(c)).containsExactly("Account: " + user1.id()).inOrder();
     }
 
     // Delete external ID by key with email
@@ -899,7 +904,7 @@
       extIdNotes.delete(extId4.accountId(), extId4.key());
       RevCommit c = extIdNotes.commit(md);
       assertThat(getFooters(c))
-          .containsExactly("Account: " + user1.getId(), "Email: foo4@example.com")
+          .containsExactly("Account: " + user1.id(), "Email: foo4@example.com")
           .inOrder();
     }
   }
@@ -928,21 +933,21 @@
       extIdNotes.insert(extId);
       try (MetaDataUpdate metaDataUpdate =
           new MetaDataUpdate(GitReferenceUpdated.DISABLED, null, repo)) {
-        metaDataUpdate.getCommitBuilder().setAuthor(admin.getIdent());
-        metaDataUpdate.getCommitBuilder().setCommitter(admin.getIdent());
+        metaDataUpdate.getCommitBuilder().setAuthor(admin.newIdent());
+        metaDataUpdate.getCommitBuilder().setCommitter(admin.newIdent());
         extIdNotes.commit(metaDataUpdate);
       }
     }
   }
 
   private void addExtId(TestRepository<?> testRepo, ExternalId... extIds)
-      throws IOException, OrmDuplicateKeyException, ConfigInvalidException {
+      throws IOException, DuplicateKeyException, ConfigInvalidException {
     ExternalIdNotes extIdNotes = externalIdNotesFactory.load(testRepo.getRepository());
     extIdNotes.insert(Arrays.asList(extIds));
     try (MetaDataUpdate metaDataUpdate =
         new MetaDataUpdate(GitReferenceUpdated.DISABLED, null, testRepo.getRepository())) {
-      metaDataUpdate.getCommitBuilder().setAuthor(admin.getIdent());
-      metaDataUpdate.getCommitBuilder().setCommitter(admin.getIdent());
+      metaDataUpdate.getCommitBuilder().setAuthor(admin.newIdent());
+      metaDataUpdate.getCommitBuilder().setCommitter(admin.newIdent());
       extIdNotes.commit(metaDataUpdate);
       extIdNotes.updateCaches();
     }
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
index 07bc394..27ae8b12 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountDetailIT.java
@@ -26,10 +26,10 @@
 public class GetAccountDetailIT extends AbstractDaemonTest {
   @Test
   public void getDetail() throws Exception {
-    RestResponse r = adminRestSession.get("/accounts/" + admin.username + "/detail/");
+    RestResponse r = adminRestSession.get("/accounts/" + admin.username() + "/detail/");
     AccountDetailInfo info = newGson().fromJson(r.getReader(), AccountDetailInfo.class);
     assertAccountInfo(admin, info);
-    Account account = getAccount(admin.getId());
+    Account account = getAccount(admin.id());
     assertThat(info.registeredOn).isEqualTo(account.getRegisteredOn());
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
index ed7abd2..11f7c0f 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/GetAccountIT.java
@@ -32,19 +32,19 @@
   @Test
   public void getAccount() throws Exception {
     // by formatted string
-    testGetAccount(admin.fullName + " <" + admin.email + ">", admin);
+    testGetAccount(admin.fullName() + " <" + admin.email() + ">", admin);
 
     // by email
-    testGetAccount(admin.email, admin);
+    testGetAccount(admin.email(), admin);
 
     // by full name
-    testGetAccount(admin.fullName, admin);
+    testGetAccount(admin.fullName(), admin);
 
     // by account ID
-    testGetAccount(Integer.toString(admin.id.get()), admin);
+    testGetAccount(Integer.toString(admin.id().get()), admin);
 
     // by user name
-    testGetAccount(admin.username, admin);
+    testGetAccount(admin.username(), admin);
 
     // by 'self'
     testGetAccount("self", admin);
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java b/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
index b001d14..4dec505 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
@@ -85,7 +85,7 @@
     admin2 = accountCreator.admin2();
     GroupInput gi = new GroupInput();
     gi.name = name("New-Group");
-    gi.members = ImmutableList.of(user.id.toString());
+    gi.members = ImmutableList.of(user.id().toString());
     newGroup = gApi.groups().create(gi).get();
   }
 
@@ -101,22 +101,22 @@
     RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
 
     ReviewInput in = ReviewInput.recommend();
-    in.onBehalfOf = user.id.toString();
+    in.onBehalfOf = user.id().toString();
     in.message = "Message on behalf of";
     revision.review(in);
 
     PatchSetApproval psa = Iterables.getOnlyElement(r.getChange().approvals().values());
     assertThat(psa.getPatchSetId().get()).isEqualTo(1);
     assertThat(psa.getLabel()).isEqualTo("Code-Review");
-    assertThat(psa.getAccountId()).isEqualTo(user.id);
+    assertThat(psa.getAccountId()).isEqualTo(user.id());
     assertThat(psa.getValue()).isEqualTo(1);
-    assertThat(psa.getRealAccountId()).isEqualTo(admin.id);
+    assertThat(psa.getRealAccountId()).isEqualTo(admin.id());
 
     ChangeData cd = r.getChange();
     ChangeMessage m = Iterables.getLast(cmUtil.byChange(cd.notes()));
     assertThat(m.getMessage()).endsWith(in.message);
-    assertThat(m.getAuthor()).isEqualTo(user.id);
-    assertThat(m.getRealAuthor()).isEqualTo(admin.id);
+    assertThat(m.getAuthor()).isEqualTo(user.id());
+    assertThat(m.getRealAuthor()).isEqualTo(admin.id());
   }
 
   @Test
@@ -126,7 +126,7 @@
     RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
 
     ReviewInput in = new ReviewInput();
-    in.onBehalfOf = user.id.toString();
+    in.onBehalfOf = user.id().toString();
     in.message = "Message on behalf of";
 
     exception.expect(AuthException.class);
@@ -141,7 +141,7 @@
 
     String changeId = createChange().getChangeId();
     ReviewInput in = new ReviewInput().label("Not-A-Label", 5);
-    in.onBehalfOf = user.id.toString();
+    in.onBehalfOf = user.id().toString();
 
     exception.expect(BadRequestException.class);
     exception.expectMessage("label \"Not-A-Label\" is not a configured label");
@@ -154,7 +154,7 @@
 
     String changeId = createChange().getChangeId();
     ReviewInput in = new ReviewInput().label("Code-Review", 1).label("Not-A-Label", 5);
-    in.onBehalfOf = user.id.toString();
+    in.onBehalfOf = user.id().toString();
     gApi.changes().id(changeId).current().review(in);
 
     assertThat(gApi.changes().id(changeId).get().labels).doesNotContainKey("Not-A-Label");
@@ -172,7 +172,7 @@
     RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
 
     ReviewInput in = new ReviewInput();
-    in.onBehalfOf = user.id.toString();
+    in.onBehalfOf = user.id().toString();
     in.label("Verified", 1);
 
     exception.expect(AuthException.class);
@@ -196,7 +196,7 @@
     PushOneCommit.Result r = createChange();
 
     ReviewInput in = new ReviewInput();
-    in.onBehalfOf = user.id.toString();
+    in.onBehalfOf = user.id().toString();
     in.label("Code-Review", 1);
     CommentInput ci = new CommentInput();
     ci.path = Patch.COMMIT_MSG;
@@ -209,15 +209,15 @@
     PatchSetApproval psa = Iterables.getOnlyElement(r.getChange().approvals().values());
     assertThat(psa.getPatchSetId().get()).isEqualTo(1);
     assertThat(psa.getLabel()).isEqualTo("Code-Review");
-    assertThat(psa.getAccountId()).isEqualTo(user.id);
+    assertThat(psa.getAccountId()).isEqualTo(user.id());
     assertThat(psa.getValue()).isEqualTo(1);
-    assertThat(psa.getRealAccountId()).isEqualTo(admin.id);
+    assertThat(psa.getRealAccountId()).isEqualTo(admin.id());
 
     ChangeData cd = r.getChange();
     Comment c = Iterables.getOnlyElement(commentsUtil.publishedByChange(cd.notes()));
     assertThat(c.message).isEqualTo(ci.message);
-    assertThat(c.author.getId()).isEqualTo(user.id);
-    assertThat(c.getRealAuthor().getId()).isEqualTo(admin.id);
+    assertThat(c.author.getId()).isEqualTo(user.id());
+    assertThat(c.getRealAuthor().getId()).isEqualTo(admin.id());
   }
 
   @Test
@@ -226,7 +226,7 @@
     PushOneCommit.Result r = createChange();
 
     ReviewInput in = new ReviewInput();
-    in.onBehalfOf = user.id.toString();
+    in.onBehalfOf = user.id().toString();
     in.label("Code-Review", 1);
     RobotCommentInput ci = new RobotCommentInput();
     ci.robotId = "my-robot";
@@ -243,8 +243,8 @@
     assertThat(c.message).isEqualTo(ci.message);
     assertThat(c.robotId).isEqualTo(ci.robotId);
     assertThat(c.robotRunId).isEqualTo(ci.robotRunId);
-    assertThat(c.author.getId()).isEqualTo(user.id);
-    assertThat(c.getRealAuthor().getId()).isEqualTo(admin.id);
+    assertThat(c.author.getId()).isEqualTo(user.id());
+    assertThat(c.getRealAuthor().getId()).isEqualTo(admin.id());
   }
 
   @Test
@@ -252,7 +252,7 @@
     allowCodeReviewOnBehalfOf();
     PushOneCommit.Result r = createChange();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     DraftInput di = new DraftInput();
     di.path = Patch.COMMIT_MSG;
     di.side = Side.REVISION;
@@ -260,9 +260,9 @@
     di.message = "message";
     gApi.changes().id(r.getChangeId()).current().createDraft(di);
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     ReviewInput in = new ReviewInput();
-    in.onBehalfOf = user.id.toString();
+    in.onBehalfOf = user.id().toString();
     in.label("Code-Review", 1);
     in.drafts = DraftHandling.PUBLISH;
 
@@ -296,11 +296,11 @@
     RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
 
     ReviewInput in = new ReviewInput();
-    in.onBehalfOf = user.id.toString();
+    in.onBehalfOf = user.id().toString();
     in.label("Code-Review", 1);
 
     exception.expect(UnprocessableEntityException.class);
-    exception.expectMessage("on_behalf_of account " + user.id + " cannot see change");
+    exception.expectMessage("on_behalf_of account " + user.id() + " cannot see change");
     revision.review(in);
   }
 
@@ -308,14 +308,14 @@
   @Test
   public void voteOnBehalfOfInvisibleUserNotAllowed() throws Exception {
     allowCodeReviewOnBehalfOf();
-    requestScopeOperations.setApiUser(accountCreator.user2().getId());
-    assertThat(accountControlFactory.get().canSee(user.id)).isFalse();
+    requestScopeOperations.setApiUser(accountCreator.user2().id());
+    assertThat(accountControlFactory.get().canSee(user.id())).isFalse();
 
     PushOneCommit.Result r = createChange();
     RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
 
     ReviewInput in = new ReviewInput();
-    in.onBehalfOf = user.id.toString();
+    in.onBehalfOf = user.id().toString();
     in.label("Code-Review", 1);
 
     exception.expect(UnprocessableEntityException.class);
@@ -331,15 +331,15 @@
     String changeId = project.get() + "~master~" + r.getChangeId();
     gApi.changes().id(changeId).current().review(ReviewInput.approve());
     SubmitInput in = new SubmitInput();
-    in.onBehalfOf = admin2.email;
+    in.onBehalfOf = admin2.email();
     gApi.changes().id(changeId).current().submit(in);
 
     ChangeData cd = r.getChange();
     assertThat(cd.change().isMerged()).isTrue();
     PatchSetApproval submitter =
         approvalsUtil.getSubmitter(cd.notes(), cd.change().currentPatchSetId());
-    assertThat(submitter.getAccountId()).isEqualTo(admin2.id);
-    assertThat(submitter.getRealAccountId()).isEqualTo(admin.id);
+    assertThat(submitter.getAccountId()).isEqualTo(admin2.id());
+    assertThat(submitter.getRealAccountId()).isEqualTo(admin.id());
   }
 
   @Test
@@ -364,7 +364,7 @@
         .current()
         .review(ReviewInput.approve());
     SubmitInput in = new SubmitInput();
-    in.onBehalfOf = admin2.email;
+    in.onBehalfOf = admin2.email();
     exception.expect(AuthException.class);
     exception.expectMessage("submit on behalf of other users not permitted");
     gApi.changes().id(project.get() + "~master~" + r.getChangeId()).current().submit(in);
@@ -379,9 +379,9 @@
     String changeId = project.get() + "~master~" + r.getChangeId();
     gApi.changes().id(changeId).current().review(ReviewInput.approve());
     SubmitInput in = new SubmitInput();
-    in.onBehalfOf = user.email;
+    in.onBehalfOf = user.email();
     exception.expect(UnprocessableEntityException.class);
-    exception.expectMessage("on_behalf_of account " + user.id + " cannot see change");
+    exception.expectMessage("on_behalf_of account " + user.id() + " cannot see change");
     gApi.changes().id(changeId).current().submit(in);
   }
 
@@ -389,14 +389,14 @@
   @Test
   public void submitOnBehalfOfInvisibleUserNotAllowed() throws Exception {
     allowSubmitOnBehalfOf();
-    requestScopeOperations.setApiUser(accountCreator.user2().getId());
-    assertThat(accountControlFactory.get().canSee(user.id)).isFalse();
+    requestScopeOperations.setApiUser(accountCreator.user2().id());
+    assertThat(accountControlFactory.get().canSee(user.id())).isFalse();
 
     PushOneCommit.Result r = createChange();
     String changeId = project.get() + "~master~" + r.getChangeId();
     gApi.changes().id(changeId).current().review(ReviewInput.approve());
     SubmitInput in = new SubmitInput();
-    in.onBehalfOf = user.email;
+    in.onBehalfOf = user.email();
     exception.expect(UnprocessableEntityException.class);
     exception.expectMessage("not found");
     exception.expectMessage(in.onBehalfOf);
@@ -406,17 +406,17 @@
   @Test
   public void runAsValidUser() throws Exception {
     allowRunAs();
-    RestResponse res = adminRestSession.getWithHeader("/accounts/self", runAsHeader(user.id));
+    RestResponse res = adminRestSession.getWithHeader("/accounts/self", runAsHeader(user.id()));
     res.assertOK();
     AccountInfo account = newGson().fromJson(res.getEntityContent(), AccountInfo.class);
-    assertThat(account._accountId).isEqualTo(user.id.get());
+    assertThat(account._accountId).isEqualTo(user.id().get());
   }
 
   @GerritConfig(name = "auth.enableRunAs", value = "false")
   @Test
   public void runAsDisabledByConfig() throws Exception {
     allowRunAs();
-    RestResponse res = adminRestSession.getWithHeader("/changes/", runAsHeader(user.id));
+    RestResponse res = adminRestSession.getWithHeader("/changes/", runAsHeader(user.id()));
     res.assertForbidden();
     assertThat(res.getEntityContent())
         .isEqualTo("X-Gerrit-RunAs disabled by auth.enableRunAs = false");
@@ -424,7 +424,7 @@
 
   @Test
   public void runAsNotPermitted() throws Exception {
-    RestResponse res = adminRestSession.getWithHeader("/changes/", runAsHeader(user.id));
+    RestResponse res = adminRestSession.getWithHeader("/changes/", runAsHeader(user.id()));
     res.assertForbidden();
     assertThat(res.getEntityContent()).isEqualTo("not permitted to use X-Gerrit-RunAs");
   }
@@ -432,7 +432,7 @@
   @Test
   public void runAsNeverPermittedForAnonymousUsers() throws Exception {
     allowRunAs();
-    RestResponse res = anonRestSession.getWithHeader("/changes/", runAsHeader(user.id));
+    RestResponse res = anonRestSession.getWithHeader("/changes/", runAsHeader(user.id()));
     res.assertForbidden();
     assertThat(res.getEntityContent()).isEqualTo("not permitted to use X-Gerrit-RunAs");
   }
@@ -450,14 +450,14 @@
     allowRunAs();
     PushOneCommit.Result r = createChange();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     DraftInput di = new DraftInput();
     di.path = Patch.COMMIT_MSG;
     di.side = Side.REVISION;
     di.line = 1;
     di.message = "inline comment";
     gApi.changes().id(r.getChangeId()).current().createDraft(di);
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
 
     // Things that aren't allowed with on_behalf_of:
     //  - no labels.
@@ -467,19 +467,21 @@
     in.drafts = DraftHandling.PUBLISH;
     RestResponse res =
         adminRestSession.postWithHeader(
-            "/changes/" + r.getChangeId() + "/revisions/current/review", runAsHeader(user.id), in);
+            "/changes/" + r.getChangeId() + "/revisions/current/review",
+            runAsHeader(user.id()),
+            in);
     res.assertOK();
 
     ChangeMessageInfo m = Iterables.getLast(gApi.changes().id(r.getChangeId()).get().messages);
     assertThat(m.message).endsWith(in.message);
-    assertThat(m.author._accountId).isEqualTo(user.id.get());
+    assertThat(m.author._accountId).isEqualTo(user.id().get());
 
     CommentInfo c =
         Iterables.getOnlyElement(gApi.changes().id(r.getChangeId()).comments().get(di.path));
-    assertThat(c.author._accountId).isEqualTo(user.id.get());
+    assertThat(c.author._accountId).isEqualTo(user.id().get());
     assertThat(c.message).isEqualTo(di.message);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertThat(gApi.changes().id(r.getChangeId()).drafts()).isEmpty();
   }
 
@@ -495,30 +497,30 @@
 
     PushOneCommit.Result r = createChange();
     ReviewInput in = new ReviewInput();
-    in.onBehalfOf = user.id.toString();
+    in.onBehalfOf = user.id().toString();
     in.message = "Message on behalf of";
 
     String endpoint = "/changes/" + r.getChangeId() + "/revisions/current/review";
-    RestResponse res = adminRestSession.postWithHeader(endpoint, runAsHeader(user2.id), in);
+    RestResponse res = adminRestSession.postWithHeader(endpoint, runAsHeader(user2.id()), in);
     res.assertForbidden();
     assertThat(res.getEntityContent())
         .isEqualTo("label required to post review on behalf of \"" + in.onBehalfOf + '"');
 
     in.label("Code-Review", 1);
-    adminRestSession.postWithHeader(endpoint, runAsHeader(user2.id), in).assertOK();
+    adminRestSession.postWithHeader(endpoint, runAsHeader(user2.id()), in).assertOK();
 
     PatchSetApproval psa = Iterables.getOnlyElement(r.getChange().approvals().values());
     assertThat(psa.getPatchSetId().get()).isEqualTo(1);
     assertThat(psa.getLabel()).isEqualTo("Code-Review");
-    assertThat(psa.getAccountId()).isEqualTo(user.id);
+    assertThat(psa.getAccountId()).isEqualTo(user.id());
     assertThat(psa.getValue()).isEqualTo(1);
-    assertThat(psa.getRealAccountId()).isEqualTo(admin.id); // not user2
+    assertThat(psa.getRealAccountId()).isEqualTo(admin.id()); // not user2
 
     ChangeData cd = r.getChange();
     ChangeMessage m = Iterables.getLast(cmUtil.byChange(cd.notes()));
     assertThat(m.getMessage()).endsWith(in.message);
-    assertThat(m.getAuthor()).isEqualTo(user.id);
-    assertThat(m.getRealAuthor()).isEqualTo(admin.id); // not user2
+    assertThat(m.getAuthor()).isEqualTo(user.id());
+    assertThat(m.getRealAuthor()).isEqualTo(admin.id()); // not user2
   }
 
   @Test
@@ -527,11 +529,11 @@
 
     PushOneCommit.Result r = createChange();
     ReviewInput in = new ReviewInput();
-    in.onBehalfOf = user.id.toString();
+    in.onBehalfOf = user.id().toString();
     in.message = "Message on behalf of";
     in.label("Code-Review", 1);
 
-    requestScopeOperations.setApiUser(accountCreator.user2().getId());
+    requestScopeOperations.setApiUser(accountCreator.user2().id());
     gApi.changes().id(r.getChangeId()).revision(r.getPatchSetId().getId()).review(in);
 
     ChangeInfo info = gApi.changes().id(r.getChangeId()).get(MESSAGES);
@@ -539,7 +541,8 @@
 
     ChangeMessageInfo changeMessageInfo = Iterables.getLast(info.messages);
     assertThat(changeMessageInfo.realAuthor).isNotNull();
-    assertThat(changeMessageInfo.realAuthor._accountId).isEqualTo(accountCreator.user2().id.get());
+    assertThat(changeMessageInfo.realAuthor._accountId)
+        .isEqualTo(accountCreator.user2().id().get());
   }
 
   private void allowCodeReviewOnBehalfOf() throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java b/javatests/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java
index ea71281..e05d0db 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/PutUsernameIT.java
@@ -27,7 +27,7 @@
     UsernameInput in = new UsernameInput();
     in.username = "myUsername";
     RestResponse r =
-        adminRestSession.put("/accounts/" + accountCreator.create().id.get() + "/username", in);
+        adminRestSession.put("/accounts/" + accountCreator.create().id().get() + "/username", in);
     r.assertOK();
     assertThat(newGson().fromJson(r.getReader(), String.class)).isEqualTo(in.username);
   }
@@ -35,9 +35,9 @@
   @Test
   public void setExisting_Conflict() throws Exception {
     UsernameInput in = new UsernameInput();
-    in.username = admin.username;
+    in.username = admin.username();
     adminRestSession
-        .put("/accounts/" + accountCreator.create().id.get() + "/username", in)
+        .put("/accounts/" + accountCreator.create().id().get() + "/username", in)
         .assertConflict();
   }
 
@@ -45,11 +45,13 @@
   public void setNew_MethodNotAllowed() throws Exception {
     UsernameInput in = new UsernameInput();
     in.username = "newUsername";
-    adminRestSession.put("/accounts/" + admin.username + "/username", in).assertMethodNotAllowed();
+    adminRestSession
+        .put("/accounts/" + admin.username() + "/username", in)
+        .assertMethodNotAllowed();
   }
 
   @Test
   public void delete_MethodNotAllowed() throws Exception {
-    adminRestSession.put("/accounts/" + admin.username + "/username").assertMethodNotAllowed();
+    adminRestSession.put("/accounts/" + admin.username() + "/username").assertMethodNotAllowed();
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java b/javatests/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java
index 3a68323..d0c1fa4 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/WatchedProjectsIT.java
@@ -58,7 +58,7 @@
 
     List<ProjectWatchInfo> persistedWatchedProjects =
         gApi.accounts().self().setWatchedProjects(projectsToWatch);
-    assertThat(persistedWatchedProjects).containsAllIn(projectsToWatch).inOrder();
+    assertThat(persistedWatchedProjects).containsAtLeastElementsIn(projectsToWatch).inOrder();
   }
 
   @Test
@@ -92,7 +92,7 @@
     List<ProjectWatchInfo> persistedWatchedProjects = gApi.accounts().self().getWatchedProjects();
 
     assertThat(persistedWatchedProjects).doesNotContain(pwi);
-    assertThat(persistedWatchedProjects).containsAllIn(projectsToWatch);
+    assertThat(persistedWatchedProjects).containsAtLeastElementsIn(projectsToWatch);
   }
 
   @Test
@@ -131,7 +131,7 @@
 
     gApi.accounts().self().setWatchedProjects(projectsToWatch);
     List<ProjectWatchInfo> persistedWatchedProjects = gApi.accounts().self().getWatchedProjects();
-    assertThat(persistedWatchedProjects).containsAllIn(projectsToWatch);
+    assertThat(persistedWatchedProjects).containsAtLeastElementsIn(projectsToWatch);
   }
 
   @Test
@@ -156,7 +156,7 @@
     String projectName = project.get();
 
     // Let another user watch a project
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     List<ProjectWatchInfo> projectsToWatch = new ArrayList<>();
 
     ProjectWatchInfo pwi = new ProjectWatchInfo();
@@ -173,7 +173,7 @@
     gApi.accounts().self().deleteWatchedProjects(d);
 
     // Check that trying to delete a non-existing watch doesn't fail
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.accounts().self().deleteWatchedProjects(d);
   }
 
@@ -182,7 +182,7 @@
     String projectName = project.get();
 
     // Let another user watch a project
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     List<ProjectWatchInfo> projectsToWatch = new ArrayList<>();
 
     ProjectWatchInfo pwi = new ProjectWatchInfo();
@@ -205,7 +205,7 @@
 
     List<ProjectWatchInfo> watchedProjects = gApi.accounts().self().getWatchedProjects();
 
-    assertThat(watchedProjects).containsAllIn(projectsToWatch);
+    assertThat(watchedProjects).containsAtLeastElementsIn(projectsToWatch);
   }
 
   @Test
@@ -239,11 +239,11 @@
     List<ProjectWatchInfo> persistedWatchedProjects = gApi.accounts().self().getWatchedProjects();
 
     assertThat(persistedWatchedProjects).doesNotContain(pwi);
-    assertThat(persistedWatchedProjects).containsAllIn(projectsToWatch);
+    assertThat(persistedWatchedProjects).containsAtLeastElementsIn(projectsToWatch);
   }
 
   @Test
   public void postWithoutBody() throws Exception {
-    adminRestSession.post("/accounts/" + admin.username + "/watched.projects").assertOK();
+    adminRestSession.post("/accounts/" + admin.username() + "/watched.projects").assertOK();
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/AccountsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/AccountsRestApiBindingsIT.java
index dd6e1a5..8e5eaa4 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/AccountsRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/AccountsRestApiBindingsIT.java
@@ -152,7 +152,7 @@
 
   @Test
   public void emailEndpoints() throws Exception {
-    execute(adminRestSession, EMAIL_ENDPOINTS, "self", admin.email);
+    execute(adminRestSession, EMAIL_ENDPOINTS, "self", admin.email());
   }
 
   @Test
@@ -166,12 +166,12 @@
         .get()
         .update(
             "Add Email",
-            admin.getId(),
+            admin.id(),
             u ->
                 u.addExternalId(
-                    ExternalId.createWithEmail(name("test"), email, admin.getId(), email)));
+                    ExternalId.createWithEmail(name("test"), email, admin.id(), email)));
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.accounts()
         .self()
         .putGpgKeys(ImmutableList.of(key.getPublicKeyArmored()), ImmutableList.of());
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
index 6addf51..55744cc 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/ChangesRestApiBindingsIT.java
@@ -280,7 +280,7 @@
     String changeId = createChange().getChangeId();
 
     AddReviewerInput addReviewerInput = new AddReviewerInput();
-    addReviewerInput.reviewer = user.email;
+    addReviewerInput.reviewer = user.email();
 
     RestApiCallHelper.execute(
         adminRestSession,
@@ -299,7 +299,7 @@
         VOTE_ENDPOINTS,
         () -> gApi.changes().id(changeId).current().review(ReviewInput.approve()),
         changeId,
-        admin.email,
+        admin.email(),
         "Code-Review");
   }
 
@@ -314,7 +314,7 @@
     String changeId = createChange().getChangeId();
 
     AddReviewerInput addReviewerInput = new AddReviewerInput();
-    addReviewerInput.reviewer = user.email;
+    addReviewerInput.reviewer = user.email();
 
     RestApiCallHelper.execute(
         adminRestSession,
@@ -335,7 +335,7 @@
         () -> gApi.changes().id(changeId).current().review(ReviewInput.approve()),
         changeId,
         "current",
-        admin.email,
+        admin.email(),
         "Code-Review");
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/GroupsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/GroupsRestApiBindingsIT.java
index b37bb01..bb12172 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/GroupsRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/GroupsRestApiBindingsIT.java
@@ -88,8 +88,8 @@
   @Test
   public void memberEndpoints() throws Exception {
     String group = gApi.groups().create("test-group").get().name;
-    gApi.groups().id(group).addMembers(admin.email);
-    RestApiCallHelper.execute(adminRestSession, MEMBER_ENDPOINTS, group, admin.email);
+    gApi.groups().id(group).addMembers(admin.email());
+    RestApiCallHelper.execute(adminRestSession, MEMBER_ENDPOINTS, group, admin.email());
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index 68622e2..f191e08 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -82,7 +82,6 @@
 import com.google.gerrit.server.validators.ValidationException;
 import com.google.gerrit.testing.ConfigSuite;
 import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -313,7 +312,7 @@
     block(p, "refs/*", Permission.SUBMIT, REGISTERED_USERS);
 
     TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
-    PushOneCommit push = pushFactory.create(admin.getIdent(), repo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), repo);
     PushOneCommit.Result result = push.to("refs/for/master");
     result.assertOkStatus();
 
@@ -333,16 +332,16 @@
     }
 
     TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
-    PushOneCommit push = pushFactory.create(admin.getIdent(), repo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), repo);
     PushOneCommit.Result result = push.to("refs/for/master");
     result.assertOkStatus();
 
     ChangeInfo change = gApi.changes().id(result.getChangeId()).get();
-    assertThat(change.owner._accountId).isEqualTo(admin.id.get());
+    assertThat(change.owner._accountId).isEqualTo(admin.id().get());
 
     submit(result.getChangeId(), new SubmitInput(), AuthException.class, "submit not permitted");
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     submit(result.getChangeId());
   }
 
@@ -359,17 +358,17 @@
     }
 
     TestRepository<InMemoryRepository> repo = cloneProject(p, admin);
-    PushOneCommit push = pushFactory.create(admin.getIdent(), repo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), repo);
     PushOneCommit.Result result = push.to("refs/for/master");
     result.assertOkStatus();
 
     ChangeInfo change = gApi.changes().id(result.getChangeId()).get();
-    assertThat(change.owner._accountId).isEqualTo(admin.id.get());
+    assertThat(change.owner._accountId).isEqualTo(admin.id().get());
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     submit(result.getChangeId(), new SubmitInput(), AuthException.class, "submit not permitted");
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     submit(result.getChangeId());
   }
 
@@ -483,7 +482,7 @@
     assertThat(log).hasSize(expectedCommitCount);
 
     assertThat(commitsInRepo)
-        .containsAllOf("Initial empty repository", "Change 1", "Change 2", "Change 3");
+        .containsAtLeast("Initial empty repository", "Change 1", "Change 2", "Change 3");
     if (getSubmitType() == SubmitType.MERGE_ALWAYS) {
       assertThat(commitsInRepo).contains("Merge changes from topic \"" + expectedTopic + "\"");
     }
@@ -580,12 +579,12 @@
     }
 
     PushOneCommit push1 =
-        pushFactory.create(admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
+        pushFactory.create(admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "a.txt", "content");
     PushOneCommit.Result c1 = push1.to("refs/heads/topic");
     c1.assertOkStatus();
     PushOneCommit push2 =
         pushFactory.create(
-            admin.getIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
+            admin.newIdent(), testRepo, PushOneCommit.SUBJECT, "b.txt", "anotherContent");
     PushOneCommit.Result c2 = push2.to("refs/heads/topic");
     c2.assertOkStatus();
 
@@ -609,10 +608,10 @@
     //
     RevCommit master = getRemoteHead(project, "master");
     PushOneCommit stableTip =
-        pushFactory.create(admin.getIdent(), testRepo, "Tip of branch stable", "stable.txt", "");
+        pushFactory.create(admin.newIdent(), testRepo, "Tip of branch stable", "stable.txt", "");
     PushOneCommit.Result stable = stableTip.to("refs/heads/stable");
     PushOneCommit mergeCommit =
-        pushFactory.create(admin.getIdent(), testRepo, "The merge commit", "merge.txt", "");
+        pushFactory.create(admin.newIdent(), testRepo, "The merge commit", "merge.txt", "");
     mergeCommit.setParents(ImmutableList.of(master, stable.getCommit()));
     PushOneCommit.Result mergeReview = mergeCommit.to("refs/for/master");
     approve(mergeReview.getChangeId());
@@ -639,11 +638,11 @@
     // push directly to stable to S1
     PushOneCommit.Result s1 =
         pushFactory
-            .create(admin.getIdent(), testRepo, "new commit into stable", "stable1.txt", "")
+            .create(admin.newIdent(), testRepo, "new commit into stable", "stable1.txt", "")
             .to("refs/heads/stable");
     // move the stable tip ahead to S2
     pushFactory
-        .create(admin.getIdent(), testRepo, "Tip of branch stable", "stable2.txt", "")
+        .create(admin.newIdent(), testRepo, "Tip of branch stable", "stable2.txt", "")
         .to("refs/heads/stable");
 
     testRepo.reset(initial);
@@ -651,12 +650,12 @@
     // move the master ahead
     PushOneCommit.Result m =
         pushFactory
-            .create(admin.getIdent(), testRepo, "Move master ahead", "master.txt", "")
+            .create(admin.newIdent(), testRepo, "Move master ahead", "master.txt", "")
             .to("refs/heads/master");
 
     // create merge change
     PushOneCommit mc =
-        pushFactory.create(admin.getIdent(), testRepo, "The merge commit", "merge.txt", "");
+        pushFactory.create(admin.newIdent(), testRepo, "The merge commit", "merge.txt", "");
     mc.setParents(ImmutableList.of(m.getCommit(), s1.getCommit()));
     PushOneCommit.Result mergeReview = mc.to("refs/for/master");
     approve(mergeReview.getChangeId());
@@ -826,7 +825,7 @@
     // Create a stable branch and bootstrap it.
     gApi.projects().name(project.get()).branch("stable").create(new BranchInput());
     PushOneCommit push =
-        pushFactory.create(user.getIdent(), testRepo, "initial commit", "a.txt", "a");
+        pushFactory.create(user.newIdent(), testRepo, "initial commit", "a.txt", "a");
     PushOneCommit.Result change = push.to("refs/heads/stable");
 
     RevCommit stable = getRemoteHead(project, "stable");
@@ -1080,7 +1079,7 @@
     assertThat(projectOperations.project(project).hasHead("master")).isFalse();
     PushOneCommit.Result change =
         pushFactory
-            .create(admin.getIdent(), testRepo, "Change 1", ImmutableMap.of())
+            .create(admin.newIdent(), testRepo, "Change 1", ImmutableMap.of())
             .to("refs/for/master");
     change.assertOkStatus();
     // TODO(dborowitz): Use EMPTY_TREE_ID after upgrading to https://git.eclipse.org/r/127473
@@ -1099,12 +1098,12 @@
   private void setChangeStatusToNew(PushOneCommit.Result... changes) throws Exception {
     for (PushOneCommit.Result change : changes) {
       try (BatchUpdate bu =
-          batchUpdateFactory.create(project, userFactory.create(admin.id), TimeUtil.nowTs())) {
+          batchUpdateFactory.create(project, userFactory.create(admin.id()), TimeUtil.nowTs())) {
         bu.addOp(
             change.getChange().getId(),
             new BatchUpdateOp() {
               @Override
-              public boolean updateChange(ChangeContext ctx) throws OrmException {
+              public boolean updateChange(ChangeContext ctx) {
                 ctx.getChange().setStatus(Change.Status.NEW);
                 ctx.getUpdate(ctx.getChange().currentPatchSetId()).setStatus(Change.Status.NEW);
                 return true;
@@ -1233,7 +1232,7 @@
     LabelInfo cr = c.labels.get("Code-Review");
     assertThat(cr.all).hasSize(1);
     assertThat(cr.all.get(0).value).isEqualTo(2);
-    assertThat(new Account.Id(cr.all.get(0)._accountId)).isEqualTo(user.getId());
+    assertThat(new Account.Id(cr.all.get(0)._accountId)).isEqualTo(user.id());
   }
 
   protected void assertMerged(String changeId) throws RestApiException {
@@ -1264,7 +1263,7 @@
         approvalsUtil.getSubmitter(cn, new PatchSet.Id(cn.getChangeId(), psId));
     assertThat(submitter).isNotNull();
     assertThat(submitter.isLegacySubmit()).isTrue();
-    assertThat(submitter.getAccountId()).isEqualTo(user.getId());
+    assertThat(submitter.getAccountId()).isEqualTo(user.id());
   }
 
   protected void assertNoSubmitter(String changeId, int psId) throws Exception {
@@ -1348,7 +1347,7 @@
 
   protected PushOneCommit.Result createChange(
       String subject, String fileName, String content, String topic) throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo, subject, fileName, content);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content);
     return push.to("refs/for/master/" + name(topic));
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
index 21d07a7..36a09fd 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByMerge.java
@@ -97,10 +97,10 @@
     assume().that(isSubmitWholeTopicEnabled()).isTrue();
     PushOneCommit.Result change1 =
         pushFactory
-            .create(admin.getIdent(), testRepo, "Change 1", "a", "a")
+            .create(admin.newIdent(), testRepo, "Change 1", "a", "a")
             .to("refs/for/master/" + name("topic"));
 
-    PushOneCommit push2 = pushFactory.create(admin.getIdent(), testRepo, "Change 2", "b", "b");
+    PushOneCommit push2 = pushFactory.create(admin.newIdent(), testRepo, "Change 2", "b", "b");
     push2.noParents();
     PushOneCommit.Result change2 = push2.to("refs/for/master/" + name("topic"));
     change2.assertOkStatus();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
index 36595ce..c12adfa 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmitByRebase.java
@@ -72,7 +72,7 @@
 
   protected ImmutableList<PushOneCommit.Result> submitWithRebase(TestAccount submitter)
       throws Exception {
-    requestScopeOperations.setApiUser(submitter.getId());
+    requestScopeOperations.setApiUser(submitter.id());
     RevCommit initialHead = getRemoteHead();
     PushOneCommit.Result change = createChange("Change 1", "a.txt", "content");
     submit(change.getChangeId());
@@ -88,8 +88,8 @@
     assertCurrentRevision(change2.getChangeId(), 2, headAfterSecondSubmit);
     assertSubmitter(change2.getChangeId(), 1, submitter);
     assertSubmitter(change2.getChangeId(), 2, submitter);
-    assertPersonEquals(admin.getIdent(), headAfterSecondSubmit.getAuthorIdent());
-    assertPersonEquals(submitter.getIdent(), headAfterSecondSubmit.getCommitterIdent());
+    assertPersonEquals(admin.newIdent(), headAfterSecondSubmit.getAuthorIdent());
+    assertPersonEquals(submitter.newIdent(), headAfterSecondSubmit.getCommitterIdent());
 
     assertRefUpdatedEvents(
         initialHead, headAfterFirstSubmit, headAfterFirstSubmit, headAfterSecondSubmit);
@@ -179,7 +179,7 @@
     PushOneCommit.Result change1 = createChange("Added a", "a.txt", "");
 
     PushOneCommit change2Push =
-        pushFactory.create(admin.getIdent(), testRepo, "Merge to master", "m.txt", "");
+        pushFactory.create(admin.newIdent(), testRepo, "Merge to master", "m.txt", "");
     change2Push.setParents(ImmutableList.of(initialHead, change1.getCommit()));
     PushOneCommit.Result change2 = change2Push.to("refs/for/master");
 
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
index bd31c5f..e7f3d54 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ActionsIT.java
@@ -160,19 +160,19 @@
     requestScopeOperations.setApiUserAnonymous();
     String etag1 = getETag(change);
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     approve(parent);
 
     requestScopeOperations.setApiUserAnonymous();
     String etag2 = getETag(change);
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     String changeWithSameTopic = createChangeWithTopic().getChangeId();
 
     requestScopeOperations.setApiUserAnonymous();
     String etag3 = getETag(change);
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     approve(changeWithSameTopic);
 
     requestScopeOperations.setApiUserAnonymous();
@@ -197,7 +197,7 @@
     requestScopeOperations.setApiUserAnonymous();
     String etag1 = getETag(change);
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     approve(parent);
 
     requestScopeOperations.setApiUserAnonymous();
@@ -328,7 +328,7 @@
     }
 
     Map<String, ActionInfo> origActions = origChange.actions;
-    assertThat(origActions.keySet()).containsAllOf("followup", "abandon");
+    assertThat(origActions.keySet()).containsAtLeast("followup", "abandon");
     assertThat(origActions.get("abandon").label).isEqualTo("Abandon");
 
     Visitor v = new Visitor();
@@ -377,7 +377,7 @@
     }
 
     Map<String, ActionInfo> origActions = gApi.changes().id(id).current().actions();
-    assertThat(origActions.keySet()).containsAllOf("cherrypick", "rebase");
+    assertThat(origActions.keySet()).containsAtLeast("cherrypick", "rebase");
     assertThat(origActions.get("rebase").label).isEqualTo("Rebase");
 
     Visitor v = new Visitor();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
index 0b39f0a..2d6227b 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AssigneeIT.java
@@ -65,31 +65,31 @@
   @Test
   public void addGetAssignee() throws Exception {
     PushOneCommit.Result r = createChange();
-    assertThat(setAssignee(r, user.email)._accountId).isEqualTo(user.getId().get());
-    assertThat(getAssignee(r)._accountId).isEqualTo(user.getId().get());
+    assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
+    assertThat(getAssignee(r)._accountId).isEqualTo(user.id().get());
 
     assertThat(sender.getMessages()).hasSize(1);
     Message m = sender.getMessages().get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress);
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
   }
 
   @Test
   public void setNewAssigneeWhenExists() throws Exception {
     PushOneCommit.Result r = createChange();
-    setAssignee(r, user.email);
-    assertThat(setAssignee(r, user.email)._accountId).isEqualTo(user.getId().get());
+    setAssignee(r, user.email());
+    assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
   }
 
   @Test
   public void getPastAssignees() throws Exception {
     PushOneCommit.Result r = createChange();
-    setAssignee(r, user.email);
-    setAssignee(r, admin.email);
+    setAssignee(r, user.email());
+    setAssignee(r, admin.email());
     List<AccountInfo> assignees = getPastAssignees(r);
     assertThat(assignees).hasSize(2);
     Iterator<AccountInfo> itr = assignees.iterator();
-    assertThat(itr.next()._accountId).isEqualTo(user.getId().get());
-    assertThat(itr.next()._accountId).isEqualTo(admin.getId().get());
+    assertThat(itr.next()._accountId).isEqualTo(user.id().get());
+    assertThat(itr.next()._accountId).isEqualTo(admin.id().get());
   }
 
   @Test
@@ -98,25 +98,25 @@
     PushOneCommit.Result r = createChange();
     Iterable<AccountInfo> reviewers = getReviewers(r, state);
     assertThat(reviewers).isNull();
-    assertThat(setAssignee(r, user.email)._accountId).isEqualTo(user.getId().get());
+    assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
     reviewers = getReviewers(r, state);
     assertThat(reviewers).hasSize(1);
     AccountInfo reviewer = Iterables.getFirst(reviewers, null);
-    assertThat(reviewer._accountId).isEqualTo(user.getId().get());
+    assertThat(reviewer._accountId).isEqualTo(user.id().get());
   }
 
   @Test
   public void setAlreadyExistingAssignee() throws Exception {
     PushOneCommit.Result r = createChange();
-    setAssignee(r, user.email);
-    assertThat(setAssignee(r, user.email)._accountId).isEqualTo(user.getId().get());
+    setAssignee(r, user.email());
+    assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
   }
 
   @Test
   public void deleteAssignee() throws Exception {
     PushOneCommit.Result r = createChange();
-    assertThat(setAssignee(r, user.email)._accountId).isEqualTo(user.getId().get());
-    assertThat(deleteAssignee(r)._accountId).isEqualTo(user.getId().get());
+    assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
+    assertThat(deleteAssignee(r)._accountId).isEqualTo(user.id().get());
     assertThat(getAssignee(r)).isNull();
   }
 
@@ -129,19 +129,19 @@
   @Test
   public void setAssigneeToInactiveUser() throws Exception {
     PushOneCommit.Result r = createChange();
-    gApi.accounts().id(user.getId().get()).setActive(false);
+    gApi.accounts().id(user.id().get()).setActive(false);
     try {
-      setAssignee(r, user.email);
+      setAssignee(r, user.email());
       assert_().fail("expected UnresolvableAccountException");
     } catch (UnresolvableAccountException e) {
       assertThat(e)
           .hasMessageThat()
           .isEqualTo(
               "Account '"
-                  + user.email
+                  + user.email()
                   + "' only matches inactive accounts. To use an inactive account, retry with one"
                   + " of the following exact account IDs:\n"
-                  + user.id
+                  + user.id()
                   + ": User <user@example.com>");
     }
   }
@@ -149,9 +149,9 @@
   @Test
   public void setAssigneeToInactiveUserById() throws Exception {
     PushOneCommit.Result r = createChange();
-    gApi.accounts().id(user.getId().get()).setActive(false);
-    setAssignee(r, user.getId().toString());
-    assertThat(getAssignee(r)._accountId).isEqualTo(user.getId().get());
+    gApi.accounts().id(user.id().get()).setActive(false);
+    setAssignee(r, user.id().toString());
+    assertThat(getAssignee(r)._accountId).isEqualTo(user.id().get());
   }
 
   @Test
@@ -161,24 +161,24 @@
     PushOneCommit.Result r = createChange("refs/for/refs/meta/config");
     exception.expect(AuthException.class);
     exception.expectMessage("read not permitted");
-    setAssignee(r, user.email);
+    setAssignee(r, user.email());
   }
 
   @Test
   public void setAssigneeNotAllowedWithoutPermission() throws Exception {
     PushOneCommit.Result r = createChange();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("not permitted");
-    setAssignee(r, user.email);
+    setAssignee(r, user.email());
   }
 
   @Test
   public void setAssigneeAllowedWithPermission() throws Exception {
     PushOneCommit.Result r = createChange();
     grant(project, "refs/heads/master", Permission.EDIT_ASSIGNEE, false, REGISTERED_USERS);
-    requestScopeOperations.setApiUser(user.getId());
-    assertThat(setAssignee(r, user.email)._accountId).isEqualTo(user.getId().get());
+    requestScopeOperations.setApiUser(user.id());
+    assertThat(setAssignee(r, user.email())._accountId).isEqualTo(user.id().get());
   }
 
   private AccountInfo getAssignee(PushOneCommit.Result r) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
index dc76585..51c5fc8 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
@@ -157,7 +157,7 @@
   @Test
   public void deleteCannotBeAppliedWithoutAdministrateServerCapability() throws Exception {
     int changeNum = createOneChangeWithMultipleChangeMessagesInHistory();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     try {
       deleteOneChangeMessage(changeNum, 0, user, "spam");
@@ -171,7 +171,7 @@
   public void deleteCanBeAppliedWithAdministrateServerCapability() throws Exception {
     allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ADMINISTRATE_SERVER);
     int changeNum = createOneChangeWithMultipleChangeMessagesInHistory();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     deleteOneChangeMessage(changeNum, 0, user, "spam");
   }
 
@@ -227,24 +227,24 @@
   private int createOneChangeWithMultipleChangeMessagesInHistory() throws Exception {
     // Creates the following commit history on the meta branch of the test change.
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     // Commit 1: create a change.
     PushOneCommit.Result result = createChange();
     String changeId = result.getChangeId();
     // Commit 2: post an empty change message.
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     addOneReviewWithEmptyChangeMessage(changeId);
     // Commit 3: post a review with message "message 1".
     addOneReview(changeId, "message 1");
     // Commit 4: amend a new patch set.
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     amendChange(changeId);
     // Commit 5: post a review with message "message 2".
     addOneReview(changeId, "message 2");
     // Commit 6: amend a new patch set.
     amendChange(changeId);
     // Commit 7: approve the change.
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(changeId).current().review(ReviewInput.approve());
     // commit 8: submit the change.
     gApi.changes().id(changeId).current().submit();
@@ -286,7 +286,7 @@
     ChangeMessageInfo info = gApi.changes().id(changeNum).message(id).delete(input);
 
     // Verify the return change message info is as expect.
-    assertThat(info.message).isEqualTo(createNewChangeMessage(deletedBy.fullName, reason));
+    assertThat(info.message).isEqualTo(createNewChangeMessage(deletedBy.fullName(), reason));
     List<ChangeMessageInfo> messagesAfterDeletion = gApi.changes().id(changeNum).messages();
     assertMessagesAfterDeletion(
         messagesBeforeDeletion, messagesAfterDeletion, deletedMessageIndex, deletedBy, reason);
@@ -326,7 +326,7 @@
 
       if (i == deletedMessageIndex) {
         assertThat(after.message)
-            .isEqualTo(createNewChangeMessage(deletedBy.fullName, deleteReason));
+            .isEqualTo(createNewChangeMessage(deletedBy.fullName(), deleteReason));
       } else {
         assertThat(after.message).isEqualTo(before.message);
       }
@@ -390,7 +390,7 @@
                 rawAfter,
                 rangeAfter.get().changeMessageStart(),
                 rangeAfter.get().changeMessageEnd() + 1);
-        assertThat(message).isEqualTo(createNewChangeMessage(deletedBy.fullName, deleteReason));
+        assertThat(message).isEqualTo(createNewChangeMessage(deletedBy.fullName(), deleteReason));
       } else {
         assertThat(commitAfter.getFullMessage()).isEqualTo(commitBefore.getFullMessage());
       }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java
index 993c10e..d51221e 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeOwnerIT.java
@@ -40,7 +40,7 @@
 
   @Before
   public void setUp() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     user2 = accountCreator.user2();
   }
 
@@ -66,22 +66,22 @@
 
   @Test
   public void testChangeOwner_OwnerACLGrantedOnParentProject() throws Exception {
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     grantApproveToChangeOwner(project);
     Project.NameKey child = projectOperations.newProject().parent(project).create();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     TestRepository<InMemoryRepository> childRepo = cloneProject(child, user);
     approve(user, createMyChange(childRepo));
   }
 
   @Test
   public void testChangeOwner_BlockedOnParentProject() throws Exception {
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     blockApproveForChangeOwner(project);
     Project.NameKey child = projectOperations.newProject().parent(project).create();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     grantApproveToAll(child);
     TestRepository<InMemoryRepository> childRepo = cloneProject(child, user);
     String changeId = createMyChange(childRepo);
@@ -95,11 +95,11 @@
 
   @Test
   public void testChangeOwner_BlockedOnParentProjectAndExclusiveAllowOnChild() throws Exception {
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     blockApproveForChangeOwner(project);
     Project.NameKey child = projectOperations.newProject().parent(project).create();
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     grantExclusiveApproveToAll(child);
     TestRepository<InMemoryRepository> childRepo = cloneProject(child, user);
     String changeId = createMyChange(childRepo);
@@ -112,7 +112,7 @@
   }
 
   private void approve(TestAccount a, String changeId) throws Exception {
-    Context old = requestScopeOperations.setApiUser(a.getId());
+    Context old = requestScopeOperations.setApiUser(a.id());
     try {
       gApi.changes().id(changeId).current().review(ReviewInput.approve());
     } finally {
@@ -147,7 +147,7 @@
   }
 
   private String createMyChange(TestRepository<InMemoryRepository> testRepo) throws Exception {
-    PushOneCommit push = pushFactory.create(user.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(user.newIdent(), testRepo);
     return push.to("refs/for/master").getChangeId();
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
index 983ad21..ac00e38 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersByEmailIT.java
@@ -74,7 +74,7 @@
   @Test
   public void addByEmailAndById() throws Exception {
     AccountInfo byEmail = new AccountInfo("Foo Bar", "foo.bar@gerritcodereview.com");
-    AccountInfo byId = new AccountInfo(user.id.get());
+    AccountInfo byId = new AccountInfo(user.id().get());
 
     for (ReviewerState state : ImmutableList.of(ReviewerState.CC, ReviewerState.REVIEWER)) {
       PushOneCommit.Result r = createChange();
@@ -85,7 +85,7 @@
       gApi.changes().id(r.getChangeId()).addReviewer(inputByEmail);
 
       AddReviewerInput inputById = new AddReviewerInput();
-      inputById.reviewer = user.email;
+      inputById.reviewer = user.email();
       inputById.state = state;
       gApi.changes().id(r.getChangeId()).addReviewer(inputById);
 
@@ -197,9 +197,9 @@
       // Review change as user
       ReviewInput reviewInput = new ReviewInput();
       reviewInput.message = "I have a comment";
-      requestScopeOperations.setApiUser(user.getId());
+      requestScopeOperations.setApiUser(user.id());
       revision(r).review(reviewInput);
-      requestScopeOperations.setApiUser(admin.getId());
+      requestScopeOperations.setApiUser(admin.id());
 
       sender.clear();
 
@@ -209,7 +209,7 @@
       List<Message> messages = sender.getMessages();
       assertThat(messages).hasSize(1);
       assertThat(messages.get(0).rcpt())
-          .containsExactly(Address.parse(addInput.reviewer), user.emailAddress);
+          .containsExactly(Address.parse(addInput.reviewer), user.getEmailAddress());
       sender.clear();
     }
   }
@@ -250,7 +250,7 @@
 
     // Also add user as a regular reviewer
     AddReviewerInput input = new AddReviewerInput();
-    input.reviewer = user.email;
+    input.reviewer = user.email();
     input.state = ReviewerState.REVIEWER;
     gApi.changes().id(r.getChangeId()).addReviewer(input);
 
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
index dec839c..173b78d 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeReviewersIT.java
@@ -80,7 +80,7 @@
     List<TestAccount> users = createAccounts(largeGroupSize, "addGroupAsReviewer");
     List<String> largeGroupUsernames = new ArrayList<>(mediumGroupSize);
     for (TestAccount u : users) {
-      largeGroupUsernames.add(u.username);
+      largeGroupUsernames.add(u.username());
     }
     List<String> mediumGroupUsernames = largeGroupUsernames.subList(0, mediumGroupSize);
     gApi.groups()
@@ -127,26 +127,26 @@
     PushOneCommit.Result r = createChange();
     String changeId = r.getChangeId();
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     in.state = CC;
     AddReviewerResult result = addReviewer(changeId, in);
 
-    assertThat(result.input).isEqualTo(user.email);
+    assertThat(result.input).isEqualTo(user.email());
     assertThat(result.confirm).isNull();
     assertThat(result.error).isNull();
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
     assertThat(result.reviewers).isNull();
     assertThat(result.ccs).hasSize(1);
     AccountInfo ai = result.ccs.get(0);
-    assertThat(ai._accountId).isEqualTo(user.id.get());
+    assertThat(ai._accountId).isEqualTo(user.id().get());
     assertReviewers(c, CC, user);
 
     // Verify email was sent to CCed account.
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress);
-    assertThat(m.body()).contains(admin.fullName + " has uploaded this change for review.");
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+    assertThat(m.body()).contains(admin.fullName() + " has uploaded this change for review.");
   }
 
   @Test
@@ -154,7 +154,7 @@
     List<TestAccount> users = createAccounts(6, "addCcGroup");
     List<String> usernames = new ArrayList<>(6);
     for (TestAccount u : users) {
-      usernames.add(u.username);
+      usernames.add(u.username());
     }
 
     List<TestAccount> firstUsers = users.subList(0, 3);
@@ -183,19 +183,19 @@
     Message m = messages.get(0);
     List<Address> expectedAddresses = new ArrayList<>(firstUsers.size());
     for (TestAccount u : firstUsers) {
-      expectedAddresses.add(u.emailAddress);
+      expectedAddresses.add(u.getEmailAddress());
     }
     assertThat(m.rcpt()).containsExactlyElementsIn(expectedAddresses);
 
     // CC a group that overlaps with some existing reviewers and CCed accounts.
     TestAccount reviewer =
         accountCreator.create(name("reviewer"), "addCcGroup-reviewer@example.com", "Reviewer");
-    result = addReviewer(changeId, reviewer.username);
+    result = addReviewer(changeId, reviewer.username());
     assertThat(result.error).isNull();
     sender.clear();
     in.reviewer = groupOperations.newGroup().name("cc2").create().get();
     gApi.groups().id(in.reviewer).addMembers(usernames.toArray(new String[usernames.size()]));
-    gApi.groups().id(in.reviewer).addMembers(reviewer.username);
+    gApi.groups().id(in.reviewer).addMembers(reviewer.username());
     result = addReviewer(changeId, in);
     assertThat(result.input).isEqualTo(in.reviewer);
     assertThat(result.confirm).isNull();
@@ -211,9 +211,9 @@
     m = messages.get(0);
     expectedAddresses = new ArrayList<>(4);
     for (int i = 0; i < 3; i++) {
-      expectedAddresses.add(users.get(users.size() - i - 1).emailAddress);
+      expectedAddresses.add(users.get(users.size() - i - 1).getEmailAddress());
     }
-    expectedAddresses.add(reviewer.emailAddress);
+    expectedAddresses.add(reviewer.getEmailAddress());
     assertThat(m.rcpt()).containsExactlyElementsIn(expectedAddresses);
   }
 
@@ -222,7 +222,7 @@
     PushOneCommit.Result r = createChange();
     String changeId = r.getChangeId();
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     in.state = CC;
     addReviewer(changeId, in);
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
@@ -263,7 +263,7 @@
     PushOneCommit.Result r = createChange();
 
     // user adds self as REVIEWER.
-    ReviewInput input = new ReviewInput().reviewer(user.username);
+    ReviewInput input = new ReviewInput().reviewer(user.username());
     RestResponse resp =
         userRestSession.post(
             "/changes/" + r.getChangeId() + "/revisions/" + r.getCommit().getName() + "/review",
@@ -282,7 +282,7 @@
     assertThat(label.all).isNotNull();
     assertThat(label.all).hasSize(1);
     ApprovalInfo approval = label.all.get(0);
-    assertThat(approval._accountId).isEqualTo(user.getId().get());
+    assertThat(approval._accountId).isEqualTo(user.id().get());
   }
 
   @Test
@@ -291,7 +291,7 @@
     PushOneCommit.Result r = createChange();
 
     // user adds self as CC.
-    ReviewInput input = new ReviewInput().reviewer(user.username, CC, false);
+    ReviewInput input = new ReviewInput().reviewer(user.username(), CC, false);
     RestResponse resp =
         userRestSession.post(
             "/changes/" + r.getChangeId() + "/revisions/" + r.getCommit().getName() + "/review",
@@ -326,7 +326,7 @@
     assertThat(label.all).isNull();
 
     // Add user as REVIEWER.
-    ReviewInput input = new ReviewInput().reviewer(user.username);
+    ReviewInput input = new ReviewInput().reviewer(user.username());
     ReviewResult result = review(r.getChangeId(), r.getCommit().name(), input);
     assertThat(result.labels).isNull();
     assertThat(result.reviewers).isNotNull();
@@ -345,8 +345,8 @@
     for (ApprovalInfo approval : label.all) {
       approvals.put(approval._accountId, approval.value);
     }
-    assertThat(approvals).containsEntry(admin.getId().get(), 0);
-    assertThat(approvals).containsEntry(user.getId().get(), 0);
+    assertThat(approvals).containsEntry(admin.id().get(), 0);
+    assertThat(approvals).containsEntry(user.id().get(), 0);
 
     // Comment as user without voting. This should delete the approval and
     // then replace it with the default value.
@@ -370,8 +370,8 @@
     for (ApprovalInfo approval : label.all) {
       approvals.put(approval._accountId, approval.value);
     }
-    assertThat(approvals).containsEntry(admin.getId().get(), 0);
-    assertThat(approvals).containsEntry(user.getId().get(), 0);
+    assertThat(approvals).containsEntry(admin.id().get(), 0);
+    assertThat(approvals).containsEntry(user.id().get(), 0);
   }
 
   @Test
@@ -379,7 +379,7 @@
     TestAccount observer = accountCreator.user2();
     PushOneCommit.Result r = createChange();
     ReviewInput input =
-        ReviewInput.approve().reviewer(user.email).reviewer(observer.email, CC, false);
+        ReviewInput.approve().reviewer(user.email()).reviewer(observer.email(), CC, false);
 
     ReviewResult result = review(r.getChangeId(), r.getCommit().name(), input);
     assertThat(result.labels).isNotNull();
@@ -397,14 +397,14 @@
     assertThat(messages).hasSize(2);
 
     Message m = messages.get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress, observer.emailAddress);
-    assertThat(m.body()).contains(admin.fullName + " has posted comments on this change.");
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress(), observer.getEmailAddress());
+    assertThat(m.body()).contains(admin.fullName() + " has posted comments on this change.");
     assertThat(m.body()).contains("Change subject: " + PushOneCommit.SUBJECT + "\n");
     assertThat(m.body()).contains("Patch Set 1: Code-Review+2");
 
     m = messages.get(1);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress, observer.emailAddress);
-    assertThat(m.body()).contains("Hello " + user.fullName + ",\n");
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress(), observer.getEmailAddress());
+    assertThat(m.body()).contains("Hello " + user.fullName() + ",\n");
     assertThat(m.body()).contains("I'd like you to do a code review.");
   }
 
@@ -415,7 +415,7 @@
     List<TestAccount> users = createAccounts(largeGroupSize, "reviewAndAddGroupReviewers");
     List<String> usernames = new ArrayList<>(largeGroupSize);
     for (TestAccount u : users) {
-      usernames.add(u.username);
+      usernames.add(u.username());
     }
 
     String largeGroup = groupOperations.newGroup().name("largeGroup").create().get();
@@ -431,8 +431,8 @@
     // Attempt to add overly large group as reviewers.
     ReviewInput input =
         ReviewInput.approve()
-            .reviewer(user.email)
-            .reviewer(observer.email, CC, false)
+            .reviewer(user.email())
+            .reviewer(observer.email(), CC, false)
             .reviewer(largeGroup);
     ReviewResult result = review(r.getChangeId(), r.getCommit().name(), input, SC_BAD_REQUEST);
     assertThat(result.labels).isNull();
@@ -454,8 +454,8 @@
     // confirmation, as reviewers.
     input =
         ReviewInput.approve()
-            .reviewer(user.email)
-            .reviewer(observer.email, CC, false)
+            .reviewer(user.email())
+            .reviewer(observer.email(), CC, false)
             .reviewer(mediumGroup);
     result = review(r.getChangeId(), r.getCommit().name(), input, SC_BAD_REQUEST);
     assertThat(result.labels).isNull();
@@ -474,7 +474,7 @@
     assertThat(c.reviewers.get(CC)).isNull();
 
     // Retrying with confirmation should successfully approve and add reviewers/CCs.
-    input = ReviewInput.approve().reviewer(user.email).reviewer(mediumGroup, CC, true);
+    input = ReviewInput.approve().reviewer(user.email()).reviewer(mediumGroup, CC, true);
     result = review(r.getChangeId(), r.getCommit().name(), input);
     assertThat(result.labels).isNotNull();
     assertThat(result.reviewers).isNotNull();
@@ -492,7 +492,7 @@
     PushOneCommit.Result r = createChange();
     String changeId = r.getChangeId();
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     in.state = CC;
     addReviewer(changeId, in);
 
@@ -501,7 +501,7 @@
 
     gApi.changes().id(changeId).current().review(ReviewInput.dislike());
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     // NoteDb adds reviewer to a change on every review.
     gApi.changes().id(changeId).current().review(ReviewInput.dislike());
 
@@ -514,28 +514,28 @@
     Iterator<ReviewerUpdateInfo> it = c.reviewerUpdates.iterator();
     ReviewerUpdateInfo reviewerChange = it.next();
     assertThat(reviewerChange.state).isEqualTo(CC);
-    assertThat(reviewerChange.reviewer._accountId).isEqualTo(user.getId().get());
-    assertThat(reviewerChange.updatedBy._accountId).isEqualTo(admin.getId().get());
+    assertThat(reviewerChange.reviewer._accountId).isEqualTo(user.id().get());
+    assertThat(reviewerChange.updatedBy._accountId).isEqualTo(admin.id().get());
 
     reviewerChange = it.next();
     assertThat(reviewerChange.state).isEqualTo(REVIEWER);
-    assertThat(reviewerChange.reviewer._accountId).isEqualTo(user.getId().get());
-    assertThat(reviewerChange.updatedBy._accountId).isEqualTo(admin.getId().get());
+    assertThat(reviewerChange.reviewer._accountId).isEqualTo(user.id().get());
+    assertThat(reviewerChange.updatedBy._accountId).isEqualTo(admin.id().get());
 
     reviewerChange = it.next();
     assertThat(reviewerChange.state).isEqualTo(REMOVED);
-    assertThat(reviewerChange.reviewer._accountId).isEqualTo(user.getId().get());
-    assertThat(reviewerChange.updatedBy._accountId).isEqualTo(admin.getId().get());
+    assertThat(reviewerChange.reviewer._accountId).isEqualTo(user.id().get());
+    assertThat(reviewerChange.updatedBy._accountId).isEqualTo(admin.id().get());
   }
 
   @Test
   public void addDuplicateReviewers() throws Exception {
     PushOneCommit.Result r = createChange();
-    ReviewInput input = ReviewInput.approve().reviewer(user.email).reviewer(user.email);
+    ReviewInput input = ReviewInput.approve().reviewer(user.email()).reviewer(user.email());
     ReviewResult result = review(r.getChangeId(), r.getCommit().name(), input);
     assertThat(result.reviewers).isNotNull();
     assertThat(result.reviewers).hasSize(1);
-    AddReviewerResult reviewerResult = result.reviewers.get(user.email);
+    AddReviewerResult reviewerResult = result.reviewers.get(user.email());
     assertThat(reviewerResult).isNotNull();
     assertThat(reviewerResult.confirm).isNull();
     assertThat(reviewerResult.error).isNull();
@@ -552,8 +552,8 @@
         accountCreator.create(name("user3"), emailPrefix + "user3@example.com", "User3");
     String group1 = groupOperations.newGroup().name("group1").create().get();
     String group2 = groupOperations.newGroup().name("group2").create().get();
-    gApi.groups().id(group1).addMembers(user1.username, user2.username);
-    gApi.groups().id(group2).addMembers(user2.username, user3.username);
+    gApi.groups().id(group1).addMembers(user1.username(), user2.username());
+    gApi.groups().id(group2).addMembers(user2.username(), user3.username());
 
     PushOneCommit.Result r = createChange();
     ReviewInput input = ReviewInput.approve().reviewer(group1).reviewer(group2);
@@ -600,7 +600,7 @@
   public void removingReviewerRemovesTheirVote() throws Exception {
     String crLabel = "Code-Review";
     PushOneCommit.Result r = createChange();
-    ReviewInput input = ReviewInput.approve().reviewer(admin.email);
+    ReviewInput input = ReviewInput.approve().reviewer(admin.email());
     ReviewResult addResult = review(r.getChangeId(), r.getCommit().name(), input);
     assertThat(addResult.reviewers).isNotNull();
     assertThat(addResult.reviewers).hasSize(1);
@@ -615,7 +615,7 @@
     assertThat(changeLabels.get(crLabel).all).isNull();
 
     // Check that the vote is gone even after the reviewer is added back
-    addReviewer(r.getChangeId(), admin.email);
+    addReviewer(r.getChangeId(), admin.email());
     changeLabels = getChangeLabels(r.getChangeId());
     assertThat(changeLabels.get(crLabel).all).isNull();
   }
@@ -626,15 +626,15 @@
     TestAccount userToNotify = createAccounts(1, "notify-details-post-review").get(0);
 
     ReviewInput reviewInput = new ReviewInput();
-    reviewInput.reviewer(user.email, ReviewerState.REVIEWER, true);
+    reviewInput.reviewer(user.email(), ReviewerState.REVIEWER, true);
     reviewInput.notify = NotifyHandling.NONE;
     reviewInput.notifyDetails =
-        ImmutableMap.of(RecipientType.TO, new NotifyInfo(ImmutableList.of(userToNotify.email)));
+        ImmutableMap.of(RecipientType.TO, new NotifyInfo(ImmutableList.of(userToNotify.email())));
 
     sender.clear();
     gApi.changes().id(r.getChangeId()).current().review(reviewInput);
     assertThat(sender.getMessages()).hasSize(1);
-    assertThat(sender.getMessages().get(0).rcpt()).containsExactly(userToNotify.emailAddress);
+    assertThat(sender.getMessages().get(0).rcpt()).containsExactly(userToNotify.getEmailAddress());
   }
 
   @Test
@@ -643,15 +643,15 @@
     TestAccount userToNotify = createAccounts(1, "notify-details-post-reviewers").get(0);
 
     AddReviewerInput addReviewer = new AddReviewerInput();
-    addReviewer.reviewer = user.email;
+    addReviewer.reviewer = user.email();
     addReviewer.notify = NotifyHandling.NONE;
     addReviewer.notifyDetails =
-        ImmutableMap.of(RecipientType.TO, new NotifyInfo(ImmutableList.of(userToNotify.email)));
+        ImmutableMap.of(RecipientType.TO, new NotifyInfo(ImmutableList.of(userToNotify.email())));
 
     sender.clear();
     gApi.changes().id(r.getChangeId()).addReviewer(addReviewer);
     assertThat(sender.getMessages()).hasSize(1);
-    assertThat(sender.getMessages().get(0).rcpt()).containsExactly(userToNotify.emailAddress);
+    assertThat(sender.getMessages().get(0).rcpt()).containsExactly(userToNotify.getEmailAddress());
   }
 
   @Test
@@ -659,12 +659,12 @@
     PushOneCommit.Result r = createChange();
     TestAccount newUser = createAccounts(1, name("foo")).get(0);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(r.getChangeId()).current().review(new ReviewInput().label("Code-Review", 1));
-    requestScopeOperations.setApiUser(newUser.getId());
+    requestScopeOperations.setApiUser(newUser.id());
     exception.expect(AuthException.class);
     exception.expectMessage("remove reviewer not permitted");
-    gApi.changes().id(r.getChangeId()).reviewer(user.email).remove();
+    gApi.changes().id(r.getChangeId()).reviewer(user.email()).remove();
   }
 
   @Test
@@ -676,10 +676,10 @@
     TestAccount newUser = createAccounts(1, name("foo")).get(0);
     grant(project, RefNames.REFS + "*", Permission.REMOVE_REVIEWER, false, REGISTERED_USERS);
 
-    gApi.changes().id(r.getChangeId()).addReviewer(user.email);
+    gApi.changes().id(r.getChangeId()).addReviewer(user.email());
     assertThatUserIsOnlyReviewer(r.getChangeId());
-    requestScopeOperations.setApiUser(newUser.getId());
-    gApi.changes().id(r.getChangeId()).reviewer(user.email).remove();
+    requestScopeOperations.setApiUser(newUser.id());
+    gApi.changes().id(r.getChangeId()).reviewer(user.email()).remove();
     assertThat(gApi.changes().id(r.getChangeId()).get().reviewers).isEmpty();
   }
 
@@ -688,11 +688,11 @@
     PushOneCommit.Result r = createChange();
     TestAccount newUser = createAccounts(1, name("foo")).get(0);
 
-    gApi.changes().id(r.getChangeId()).addReviewer(user.email);
-    requestScopeOperations.setApiUser(newUser.getId());
+    gApi.changes().id(r.getChangeId()).addReviewer(user.email());
+    requestScopeOperations.setApiUser(newUser.id());
     exception.expect(AuthException.class);
     exception.expectMessage("remove reviewer not permitted");
-    gApi.changes().id(r.getChangeId()).reviewer(user.email).remove();
+    gApi.changes().id(r.getChangeId()).reviewer(user.email()).remove();
   }
 
   @Test
@@ -701,13 +701,13 @@
     TestAccount newUser = createAccounts(1, name("foo")).get(0);
 
     AddReviewerInput input = new AddReviewerInput();
-    input.reviewer = user.email;
+    input.reviewer = user.email();
     input.state = ReviewerState.CC;
     gApi.changes().id(r.getChangeId()).addReviewer(input);
-    requestScopeOperations.setApiUser(newUser.getId());
+    requestScopeOperations.setApiUser(newUser.id());
     exception.expect(AuthException.class);
     exception.expectMessage("remove reviewer not permitted");
-    gApi.changes().id(r.getChangeId()).reviewer(user.email).remove();
+    gApi.changes().id(r.getChangeId()).reviewer(user.email()).remove();
   }
 
   @Test
@@ -715,13 +715,13 @@
     PushOneCommit.Result r = createChange();
 
     AddReviewerInput input = new AddReviewerInput();
-    input.reviewer = user.email;
+    input.reviewer = user.email();
     input.state = ReviewerState.REVIEWER;
 
     AddReviewerResult result = gApi.changes().id(r.getChangeId()).addReviewer(input);
     assertThat(result.reviewers).hasSize(1);
     ReviewerInfo info = result.reviewers.get(0);
-    assertThat(info._accountId).isEqualTo(user.id.get());
+    assertThat(info._accountId).isEqualTo(user.id().get());
 
     assertThat(gApi.changes().id(r.getChangeId()).addReviewer(input).reviewers).isEmpty();
   }
@@ -731,21 +731,21 @@
     PushOneCommit.Result r = createChange();
 
     AddReviewerInput input = new AddReviewerInput();
-    input.reviewer = user.email;
+    input.reviewer = user.email();
     input.state = ReviewerState.CC;
 
     AddReviewerResult result = gApi.changes().id(r.getChangeId()).addReviewer(input);
     assertThat(result.ccs).hasSize(1);
     AccountInfo info = result.ccs.get(0);
-    assertThat(info._accountId).isEqualTo(user.id.get());
+    assertThat(info._accountId).isEqualTo(user.id().get());
 
     assertThat(gApi.changes().id(r.getChangeId()).addReviewer(input).ccs).isEmpty();
   }
 
   private void assertThatUserIsOnlyReviewer(String changeId) throws Exception {
-    AccountInfo userInfo = new AccountInfo(user.fullName, user.emailAddress.getEmail());
-    userInfo._accountId = user.id.get();
-    userInfo.username = user.username;
+    AccountInfo userInfo = new AccountInfo(user.fullName(), user.getEmailAddress().getEmail());
+    userInfo._accountId = user.id().get();
+    userInfo.username = user.username();
     assertThat(gApi.changes().id(changeId).get().reviewers)
         .containsExactly(ReviewerState.REVIEWER, ImmutableList.of(userInfo));
   }
@@ -772,7 +772,7 @@
   }
 
   private RestResponse deleteReviewer(String changeId, TestAccount account) throws Exception {
-    return adminRestSession.delete("/changes/" + changeId + "/reviewers/" + account.getId().get());
+    return adminRestSession.delete("/changes/" + changeId + "/reviewers/" + account.id().get());
   }
 
   private ReviewResult review(String changeId, String revisionId, ReviewInput in) throws Exception {
@@ -816,7 +816,7 @@
     }
     List<Integer> expectedAccountIds = new ArrayList<>();
     for (TestAccount account : accounts) {
-      expectedAccountIds.add(account.getId().get());
+      expectedAccountIds.add(account.id().get());
     }
     assertThat(actualAccountIds).containsExactlyElementsIn(expectedAccountIds);
   }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java
index b6547f0..9a907aa 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ConfigChangeIT.java
@@ -56,7 +56,7 @@
       u.save();
     }
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     fetchRefsMetaConfig();
   }
 
@@ -100,13 +100,13 @@
   @Test
   @TestProjectInput(cloneAs = "user")
   public void onlyAdminMayUpdateProjectParent() throws Exception {
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     ProjectInput parent = new ProjectInput();
     parent.name = name("parent");
     parent.permissionsOnly = true;
     gApi.projects().create(parent);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     Config cfg = readProjectConfig();
     assertThat(cfg.getString("access", null, "inheritFrom")).isAnyOf(null, allProjects.get());
     cfg.setString("access", null, "inheritFrom", parent.name);
@@ -136,7 +136,7 @@
     assertThat(readProjectConfig().getString("access", null, "inheritFrom"))
         .isAnyOf(null, allProjects.get());
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(id).current().submit();
     assertThat(gApi.changes().id(id).info().status).isEqualTo(ChangeStatus.MERGED);
     assertThat(gApi.projects().name(project.get()).get().parent).isEqualTo(parent.name);
@@ -146,7 +146,7 @@
 
   @Test
   public void rejectDoubleInheritance() throws Exception {
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     // Create separate projects to test the config
     Project.NameKey parent = createProjectOverAPI("projectToInheritFrom", null, true, null);
     Project.NameKey child = createProjectOverAPI("projectWithMalformedConfig", null, true, null);
@@ -168,7 +168,7 @@
     GitUtil.fetch(childRepo, RefNames.REFS_CONFIG + ":cfg");
     childRepo.reset("cfg");
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), childRepo, "Subject", "project.config", config);
+        pushFactory.create(admin.newIdent(), childRepo, "Subject", "project.config", config);
     PushOneCommit.Result res = push.to(RefNames.REFS_CONFIG);
     res.assertErrorStatus();
     res.assertMessage("cannot inherit from multiple projects");
@@ -194,7 +194,7 @@
     PushOneCommit.Result r =
         pushFactory
             .create(
-                user.getIdent(), testRepo, "Update project config", "project.config", cfg.toText())
+                user.newIdent(), testRepo, "Update project config", "project.config", cfg.toText())
             .to("refs/for/refs/meta/config");
     r.assertOkStatus();
     return r;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CorsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CorsIT.java
index 6fd3bab..9c42542 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CorsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CorsIT.java
@@ -205,7 +205,7 @@
     BasicCookieStore cookies = new BasicCookieStore();
     Executor http = Executor.newInstance().cookieStore(cookies);
 
-    Request req = Request.Get(canonicalWebUrl.get() + "/login/?account_id=" + admin.id.get());
+    Request req = Request.Get(canonicalWebUrl.get() + "/login/?account_id=" + admin.id().get());
     http.execute(req);
     String auth = null;
     for (Cookie c : cookies.getCookies()) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
index 9a6b823..2a8293f 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -154,18 +154,18 @@
 
   @Test
   public void notificationsOnChangeCreation() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     watch(project.get());
 
     // check that watcher is notified
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     assertCreateSucceeds(newChangeInput(ChangeStatus.NEW));
 
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress);
-    assertThat(m.body()).contains(admin.fullName + " has uploaded this change for review.");
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
+    assertThat(m.body()).contains(admin.fullName() + " has uploaded this change for review.");
 
     // check that watcher is not notified if notify=NONE
     sender.clear();
@@ -184,7 +184,7 @@
       assertThat(message)
           .contains(
               String.format(
-                  "%sAdministrator <%s>", SIGNED_OFF_BY_TAG, admin.getIdent().getEmailAddress()));
+                  "%sAdministrator <%s>", SIGNED_OFF_BY_TAG, admin.newIdent().getEmailAddress()));
     } finally {
       setSignedOffByFooter(false);
     }
@@ -205,7 +205,7 @@
       assertThat(message)
           .contains(
               String.format(
-                  "%sAdministrator <%s>", SIGNED_OFF_BY_TAG, admin.getIdent().getEmailAddress()));
+                  "%sAdministrator <%s>", SIGNED_OFF_BY_TAG, admin.newIdent().getEmailAddress()));
     } finally {
       setSignedOffByFooter(false);
     }
@@ -279,7 +279,7 @@
       assertThat(commit.getShortMessage()).isEqualTo("Create change");
 
       PersonIdent expectedAuthor =
-          changeNoteUtil.newIdent(getAccount(admin.id), c.created, serverIdent.get());
+          changeNoteUtil.newIdent(getAccount(admin.id()), c.created, serverIdent.get());
       assertThat(commit.getAuthorIdent()).isEqualTo(expectedAuthor);
 
       assertThat(commit.getCommitterIdent())
@@ -451,7 +451,7 @@
   public void createChangeOnExistingBranchNotPermitted() throws Exception {
     createBranch(new Branch.NameKey(project, "foo"));
     blockRead("refs/heads/*");
-    requestScopeOperations.setApiUser(user.id);
+    requestScopeOperations.setApiUser(user.id());
     ChangeInput input = newChangeInput(ChangeStatus.NEW);
     input.branch = "foo";
 
@@ -461,7 +461,7 @@
   @Test
   public void createChangeOnNonExistingBranchNotPermitted() throws Exception {
     blockRead("refs/heads/*");
-    requestScopeOperations.setApiUser(user.id);
+    requestScopeOperations.setApiUser(user.id());
     ChangeInput input = newChangeInput(ChangeStatus.NEW);
     input.branch = "foo";
     // sets this option to be true to make sure permission check happened before this option could
@@ -514,12 +514,12 @@
 
   // TODO(davido): Expose setting of account preferences in the API
   private void setSignedOffByFooter(boolean value) throws Exception {
-    RestResponse r = adminRestSession.get("/accounts/" + admin.email + "/preferences");
+    RestResponse r = adminRestSession.get("/accounts/" + admin.email() + "/preferences");
     r.assertOK();
     GeneralPreferencesInfo i = newGson().fromJson(r.getReader(), GeneralPreferencesInfo.class);
     i.signedOffBy = value;
 
-    r = adminRestSession.put("/accounts/" + admin.email + "/preferences", i);
+    r = adminRestSession.put("/accounts/" + admin.email() + "/preferences", i);
     r.assertOK();
     GeneralPreferencesInfo o = newGson().fromJson(r.getReader(), GeneralPreferencesInfo.class);
 
@@ -563,7 +563,7 @@
     // create a initial commit in master
     Result initialCommit =
         pushFactory
-            .create(user.getIdent(), testRepo, "initial commit", "readme.txt", "initial commit")
+            .create(user.newIdent(), testRepo, "initial commit", "readme.txt", "initial commit")
             .to("refs/heads/master");
     initialCommit.assertOkStatus();
 
@@ -574,13 +574,13 @@
     // create a commit in branchA
     Result changeA =
         pushFactory
-            .create(user.getIdent(), testRepo, "change A", fileA, "A content")
+            .create(user.newIdent(), testRepo, "change A", fileA, "A content")
             .to("refs/heads/" + branchA);
     changeA.assertOkStatus();
 
     // create a commit in branchB
     PushOneCommit commitB =
-        pushFactory.create(user.getIdent(), testRepo, "change B", fileB, "B content");
+        pushFactory.create(user.newIdent(), testRepo, "change B", fileB, "B content");
     commitB.setParent(initialCommit.getCommit());
     Result changeB = commitB.to("refs/heads/" + branchB);
     changeB.assertOkStatus();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java b/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
index 1c1c455..b0fad7e 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/DeleteVoteIT.java
@@ -55,7 +55,7 @@
 
     PushOneCommit.Result r2 = amendChange(r.getChangeId());
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     recommend(r.getChangeId());
 
     sender.clear();
@@ -64,7 +64,7 @@
             + r.getChangeId()
             + (onRevisionLevel ? ("/revisions/" + r2.getCommit().getName()) : "")
             + "/reviewers/"
-            + user.getId().toString()
+            + user.id().toString()
             + "/votes/Code-Review";
 
     RestResponse response = adminRestSession.delete(endPoint);
@@ -73,17 +73,17 @@
     List<FakeEmailSender.Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     FakeEmailSender.Message msg = messages.get(0);
-    assertThat(msg.rcpt()).containsExactly(user.emailAddress);
-    assertThat(msg.body()).contains(admin.fullName + " has removed a vote on this change.\n");
+    assertThat(msg.rcpt()).containsExactly(user.getEmailAddress());
+    assertThat(msg.body()).contains(admin.fullName() + " has removed a vote on this change.\n");
     assertThat(msg.body())
-        .contains("Removed Code-Review+1 by " + user.fullName + " <" + user.email + ">\n");
+        .contains("Removed Code-Review+1 by " + user.fullName() + " <" + user.email() + ">\n");
 
     endPoint =
         "/changes/"
             + r.getChangeId()
             + (onRevisionLevel ? ("/revisions/" + r2.getCommit().getName()) : "")
             + "/reviewers/"
-            + user.getId().toString()
+            + user.id().toString()
             + "/votes";
 
     response = adminRestSession.get(endPoint);
@@ -97,10 +97,10 @@
     ChangeInfo c = gApi.changes().id(r.getChangeId()).get();
 
     ChangeMessageInfo message = Iterables.getLast(c.messages);
-    assertThat(message.author._accountId).isEqualTo(admin.getId().get());
+    assertThat(message.author._accountId).isEqualTo(admin.id().get());
     assertThat(message.message).isEqualTo("Removed Code-Review+1 by User <user@example.com>\n");
     assertThat(getReviewers(c.reviewers.get(REVIEWER)))
-        .containsExactlyElementsIn(ImmutableSet.of(admin.getId(), user.getId()));
+        .containsExactlyElementsIn(ImmutableSet.of(admin.id(), user.id()));
   }
 
   private Iterable<Account.Id> getReviewers(Collection<AccountInfo> r) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java
index 2b0ba4e..c13df49 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/HashtagsIT.java
@@ -257,7 +257,7 @@
   @Test
   public void addHashtagWithoutPermissionNotAllowed() throws Exception {
     PushOneCommit.Result r = createChange();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("edit hashtags not permitted");
     addHashtags(r, "MyHashtag");
@@ -267,7 +267,7 @@
   public void addHashtagWithPermissionAllowed() throws Exception {
     PushOneCommit.Result r = createChange();
     grant(project, "refs/heads/master", Permission.EDIT_HASHTAGS, false, REGISTERED_USERS);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     addHashtags(r, "MyHashtag");
     assertThatGet(r).containsExactly("MyHashtag");
     assertMessage(r, "Hashtag added: MyHashtag");
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/IndexChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/IndexChangeIT.java
index cf5136b..f49d1fb 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/IndexChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/IndexChangeIT.java
@@ -58,7 +58,7 @@
     TestAccount user2 = accountCreator.user2();
     AccountGroup.UUID groupId = groupOperations.newGroup().name("test").create();
     String group = groupOperations.group(groupId).get().name();
-    gApi.groups().id(group).addMembers("admin", "user", user2.username);
+    gApi.groups().id(group).addMembers("admin", "user", user2.username());
 
     // Create a project and restrict its visibility to the group
     Project.NameKey p = projectOperations.newProject().create();
@@ -74,39 +74,39 @@
 
     // Clone it and push a change as a regular user
     TestRepository<InMemoryRepository> repo = cloneProject(p, user);
-    PushOneCommit push = pushFactory.create(user.getIdent(), repo);
+    PushOneCommit push = pushFactory.create(user.newIdent(), repo);
     PushOneCommit.Result result = push.to("refs/for/master");
     result.assertOkStatus();
-    assertThat(result.getChange().change().getOwner()).isEqualTo(user.id);
+    assertThat(result.getChange().change().getOwner()).isEqualTo(user.id());
     String changeId = result.getChangeId();
 
     // User can see the change and it is mergeable
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     List<ChangeInfo> changes = gApi.changes().query(changeId).get();
     assertThat(changes).hasSize(1);
     assertThat(changes.get(0).mergeable).isNotNull();
 
     // Other user can see the change and it is mergeable
-    requestScopeOperations.setApiUser(user2.getId());
+    requestScopeOperations.setApiUser(user2.id());
     changes = gApi.changes().query(changeId).get();
     assertThat(changes).hasSize(1);
     assertThat(changes.get(0).mergeable).isTrue();
 
     // Remove the user from the group so they can no longer see the project
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.groups().id(group).removeMembers("user");
 
     // User can no longer see the change
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     changes = gApi.changes().query(changeId).get();
     assertThat(changes).isEmpty();
 
     // Reindex the change
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(changeId).index();
 
     // Other user can still see the change and it is still mergeable
-    requestScopeOperations.setApiUser(user2.getId());
+    requestScopeOperations.setApiUser(user2.id());
     changes = gApi.changes().query(changeId).get();
     assertThat(changes).hasSize(1);
     assertThat(changes.get(0).mergeable).isTrue();
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ListChangesOptionsIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ListChangesOptionsIT.java
index ae6c14c..0db3508 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/ListChangesOptionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ListChangesOptionsIT.java
@@ -48,7 +48,7 @@
     String subject = "Change subject";
     String fileName = "a.txt";
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), testRepo, subject, fileName, content, baseChangeId);
+        pushFactory.create(admin.newIdent(), testRepo, subject, fileName, content, baseChangeId);
     PushOneCommit.Result r = push.to("refs/for/master");
     r.assertOkStatus();
     return r;
@@ -65,7 +65,7 @@
   public void currentRevision() throws Exception {
     ChangeInfo c = get(changeId, CURRENT_REVISION);
     assertThat(c.currentRevision).isEqualTo(commitId(2));
-    assertThat(c.revisions.keySet()).containsAllIn(ImmutableSet.of(commitId(2)));
+    assertThat(c.revisions.keySet()).containsAtLeastElementsIn(ImmutableSet.of(commitId(2)));
     assertThat(c.revisions.get(commitId(2))._number).isEqualTo(3);
   }
 
@@ -74,7 +74,7 @@
     ChangeInfo c = get(changeId, CURRENT_REVISION, MESSAGES);
     assertThat(c.revisions).hasSize(1);
     assertThat(c.currentRevision).isEqualTo(commitId(2));
-    assertThat(c.revisions.keySet()).containsAllIn(ImmutableSet.of(commitId(2)));
+    assertThat(c.revisions.keySet()).containsAtLeastElementsIn(ImmutableSet.of(commitId(2)));
     assertThat(c.revisions.get(commitId(2))._number).isEqualTo(3);
   }
 
@@ -83,7 +83,7 @@
     ChangeInfo c = get(changeId, ALL_REVISIONS);
     assertThat(c.currentRevision).isEqualTo(commitId(2));
     assertThat(c.revisions.keySet())
-        .containsAllIn(ImmutableSet.of(commitId(0), commitId(1), commitId(2)));
+        .containsAtLeastElementsIn(ImmutableSet.of(commitId(0), commitId(1), commitId(2)));
     assertThat(c.revisions.get(commitId(0))._number).isEqualTo(1);
     assertThat(c.revisions.get(commitId(1))._number).isEqualTo(2);
     assertThat(c.revisions.get(commitId(2))._number).isEqualTo(3);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java
index 7d97967..2448ff881c 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java
@@ -147,8 +147,8 @@
         .parent(r1.getCommit())
         .parent(r2.getCommit())
         .message("Move change Merge Commit")
-        .author(admin.getIdent())
-        .committer(new PersonIdent(admin.getIdent(), testRepo.getDate()));
+        .author(admin.newIdent())
+        .committer(new PersonIdent(admin.newIdent(), testRepo.getDate()));
     RevCommit c = commitBuilder.create();
     pushHead(testRepo, "refs/for/master", false, false);
 
@@ -186,7 +186,7 @@
         r.getChange().change().getDest().get(),
         Permission.ABANDON,
         systemGroupBackend.getGroup(REGISTERED_USERS).getUUID());
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("move not permitted");
     move(r.getChangeId(), newBranch.get());
@@ -279,9 +279,9 @@
     input.label(testLabelC, -1);
     gApi.changes().id(changeId).current().review(input);
 
-    assertThat(gApi.changes().id(changeId).current().reviewer(admin.email).votes().keySet())
+    assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().keySet())
         .containsExactly(codeReviewLabel, testLabelA, testLabelB, testLabelC);
-    assertThat(gApi.changes().id(changeId).current().reviewer(admin.email).votes().values())
+    assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().values())
         .containsExactly((short) -2, (short) -1, (short) -1, (short) -1);
 
     // Move the change to the 'foo' branch.
@@ -290,13 +290,13 @@
     assertThat(gApi.changes().id(changeId).get().branch).isEqualTo("foo");
 
     // 'Code-Review -2' and 'Label-A -1' will be kept.
-    assertThat(gApi.changes().id(changeId).current().reviewer(admin.email).votes().values())
+    assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().values())
         .containsExactly((short) -2, (short) -1, (short) 0, (short) 0);
 
     // Move the change back to 'master'.
     move(changeId, "master");
     assertThat(gApi.changes().id(changeId).get().branch).isEqualTo("master");
-    assertThat(gApi.changes().id(changeId).current().reviewer(admin.email).votes().values())
+    assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().values())
         .containsExactly((short) -2, (short) -1, (short) 0, (short) 0);
   }
 
@@ -319,9 +319,9 @@
     input.label(testLabelA, -1);
     gApi.changes().id(changeId).current().review(input);
 
-    assertThat(gApi.changes().id(changeId).current().reviewer(admin.email).votes().keySet())
+    assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().keySet())
         .containsExactly(testLabelA);
-    assertThat(gApi.changes().id(changeId).current().reviewer(admin.email).votes().values())
+    assertThat(gApi.changes().id(changeId).current().reviewer(admin.email()).votes().values())
         .containsExactly((short) -1);
 
     move(changeId, "foo");
@@ -364,7 +364,7 @@
   }
 
   private PushOneCommit.Result createChange(String branch, String changeId) throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo, changeId);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo, changeId);
     PushOneCommit.Result result = push.to("refs/for/" + branch);
     result.assertOkStatus();
     return result;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java b/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java
index 82fc426..ca4288f6 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/PrivateByDefaultIT.java
@@ -121,7 +121,7 @@
 
     TestRepository<InMemoryRepository> testRepo = cloneProject(project2);
     PushOneCommit.Result result =
-        pushFactory.create(admin.getIdent(), testRepo).to("refs/for/master%private");
+        pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%private");
     result.assertErrorStatus();
   }
 
@@ -133,11 +133,11 @@
     RevCommit initialHead = getRemoteHead(project2, "master");
     TestRepository<InMemoryRepository> testRepo = cloneProject(project2);
     PushOneCommit.Result result =
-        pushFactory.create(admin.getIdent(), testRepo).to("refs/for/master%draft");
+        pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master%draft");
     result.assertErrorStatus();
 
     testRepo.reset(initialHead);
-    result = pushFactory.create(admin.getIdent(), testRepo).to("refs/drafts/master");
+    result = pushFactory.create(admin.newIdent(), testRepo).to("refs/drafts/master");
     result.assertErrorStatus();
   }
 
@@ -154,7 +154,7 @@
 
   private PushOneCommit.Result createChange(Project.NameKey proj, String ref) throws Exception {
     TestRepository<InMemoryRepository> testRepo = cloneProject(proj);
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result result = push.to(ref);
     result.assertOkStatus();
     return result;
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
index cde75a0..2ad4aca 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByCherryPickIT.java
@@ -76,8 +76,8 @@
     assertCurrentRevision(change2.getChangeId(), 2, newHead);
     assertSubmitter(change2.getChangeId(), 1);
     assertSubmitter(change2.getChangeId(), 2);
-    assertPersonEquals(admin.getIdent(), newHead.getAuthorIdent());
-    assertPersonEquals(admin.getIdent(), newHead.getCommitterIdent());
+    assertPersonEquals(admin.newIdent(), newHead.getAuthorIdent());
+    assertPersonEquals(admin.newIdent(), newHead.getCommitterIdent());
 
     assertRefUpdatedEvents(initialHead, headAfterFirstSubmit, headAfterFirstSubmit, newHead);
     assertChangeMergedEvents(
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
index ccb684c..974180c 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByFastForwardIT.java
@@ -70,8 +70,8 @@
     assertSubmitter(change.getChangeId(), 1);
     assertSubmitter(change2.getChangeId(), 1);
     assertSubmitter(change3.getChangeId(), 1);
-    assertPersonEquals(admin.getIdent(), updatedHead.getAuthorIdent());
-    assertPersonEquals(admin.getIdent(), updatedHead.getCommitterIdent());
+    assertPersonEquals(admin.newIdent(), updatedHead.getAuthorIdent());
+    assertPersonEquals(admin.newIdent(), updatedHead.getCommitterIdent());
     assertSubmittedTogether(id1, id3, id2, id1);
     assertSubmittedTogether(id2, id3, id2, id1);
     assertSubmittedTogether(id3, id3, id2, id1);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java
index 4af27ab..9bc5a2f 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeAlwaysIT.java
@@ -38,7 +38,7 @@
     assertThat(headAfterSubmit.getParent(0)).isEqualTo(initialHead);
     assertThat(headAfterSubmit.getParent(1)).isEqualTo(change.getCommit());
     assertSubmitter(change.getChangeId(), 1);
-    assertPersonEquals(admin.getIdent(), headAfterSubmit.getAuthorIdent());
+    assertPersonEquals(admin.newIdent(), headAfterSubmit.getAuthorIdent());
     assertPersonEquals(serverIdent.get(), headAfterSubmit.getCommitterIdent());
 
     assertRefUpdatedEvents(initialHead, headAfterSubmit);
@@ -82,7 +82,7 @@
     assertThat(headAfterSecondSubmit.getParent(0).getShortMessage())
         .isEqualTo(headAfterFirstSubmit.getShortMessage());
     assertThat(headAfterSecondSubmit.getParent(0).getId()).isEqualTo(headAfterFirstSubmit.getId());
-    assertPersonEquals(admin.getIdent(), headAfterSecondSubmit.getAuthorIdent());
+    assertPersonEquals(admin.newIdent(), headAfterSecondSubmit.getAuthorIdent());
     assertPersonEquals(serverIdent.get(), headAfterSecondSubmit.getCommitterIdent());
 
     assertRefUpdatedEvents(
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
index 147abe1..5ebcd85 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByMergeIfNecessaryIT.java
@@ -71,8 +71,8 @@
     assertThat(updatedHead.getId()).isEqualTo(change.getCommit());
     assertThat(updatedHead.getParent(0)).isEqualTo(initialHead);
     assertSubmitter(change.getChangeId(), 1);
-    assertPersonEquals(admin.getIdent(), updatedHead.getAuthorIdent());
-    assertPersonEquals(admin.getIdent(), updatedHead.getCommitterIdent());
+    assertPersonEquals(admin.newIdent(), updatedHead.getAuthorIdent());
+    assertPersonEquals(admin.newIdent(), updatedHead.getCommitterIdent());
 
     assertRefUpdatedEvents(initialHead, updatedHead);
     assertChangeMergedEvents(change.getChangeId(), updatedHead.name());
@@ -100,8 +100,8 @@
     assertThat(headAfterFirstSubmit.getShortMessage())
         .isEqualTo(change2.getCommit().getShortMessage());
     assertThat(headAfterFirstSubmit.getParent(0).getId()).isEqualTo(initialHead.getId());
-    assertPersonEquals(admin.getIdent(), headAfterFirstSubmit.getAuthorIdent());
-    assertPersonEquals(admin.getIdent(), headAfterFirstSubmit.getCommitterIdent());
+    assertPersonEquals(admin.newIdent(), headAfterFirstSubmit.getAuthorIdent());
+    assertPersonEquals(admin.newIdent(), headAfterFirstSubmit.getCommitterIdent());
 
     // We need to merge changes 3, 4 and 5.
     approve(change3.getChangeId());
@@ -114,7 +114,7 @@
     assertThat(headAfterSecondSubmit.getParent(0).getShortMessage())
         .isEqualTo(change2.getCommit().getShortMessage());
 
-    assertPersonEquals(admin.getIdent(), headAfterSecondSubmit.getAuthorIdent());
+    assertPersonEquals(admin.newIdent(), headAfterSecondSubmit.getAuthorIdent());
     assertPersonEquals(serverIdent.get(), headAfterSecondSubmit.getCommitterIdent());
 
     // First change stays untouched.
@@ -445,7 +445,7 @@
     gApi.projects().name(project.get()).branch("stable").create(new BranchInput());
 
     // Push a change to master
-    PushOneCommit push = pushFactory.create(user.getIdent(), testRepo, "small fix", "a.txt", "2");
+    PushOneCommit push = pushFactory.create(user.newIdent(), testRepo, "small fix", "a.txt", "2");
     PushOneCommit.Result change = push.to("refs/for/master");
     submit(change.getChangeId());
     RevCommit headAfterFirstSubmit = getRemoteLog(project, "master").get(0);
@@ -497,7 +497,7 @@
     gApi.projects().name(project.get()).branch("stable").create(new BranchInput());
 
     // Propose a change for master, but leave it open for master!
-    PushOneCommit change = pushFactory.create(user.getIdent(), testRepo, "small fix", "a.txt", "2");
+    PushOneCommit change = pushFactory.create(user.newIdent(), testRepo, "small fix", "a.txt", "2");
     PushOneCommit.Result change2result = change.to("refs/for/master");
 
     // Now cherry pick to stable
@@ -534,13 +534,13 @@
   @Test
   public void dependencyOnOutdatedPatchSetPreventsMerge() throws Exception {
     // Create a change
-    PushOneCommit change = pushFactory.create(user.getIdent(), testRepo, "fix", "a.txt", "foo");
+    PushOneCommit change = pushFactory.create(user.newIdent(), testRepo, "fix", "a.txt", "foo");
     PushOneCommit.Result changeResult = change.to("refs/for/master");
     PatchSet.Id patchSetId = changeResult.getPatchSetId();
 
     // Create a successor change.
     PushOneCommit change2 =
-        pushFactory.create(user.getIdent(), testRepo, "feature", "b.txt", "bar");
+        pushFactory.create(user.newIdent(), testRepo, "feature", "b.txt", "bar");
     PushOneCommit.Result change2Result = change2.to("refs/for/master");
 
     // Create new patch set for first change.
@@ -576,12 +576,12 @@
   @Test
   public void dependencyOnDeletedChangePreventsMerge() throws Exception {
     // Create a change
-    PushOneCommit change = pushFactory.create(user.getIdent(), testRepo, "fix", "a.txt", "foo");
+    PushOneCommit change = pushFactory.create(user.newIdent(), testRepo, "fix", "a.txt", "foo");
     PushOneCommit.Result changeResult = change.to("refs/for/master");
 
     // Create a successor change.
     PushOneCommit change2 =
-        pushFactory.create(user.getIdent(), testRepo, "feature", "b.txt", "bar");
+        pushFactory.create(user.newIdent(), testRepo, "feature", "b.txt", "bar");
     PushOneCommit.Result change2Result = change2.to("refs/for/master");
 
     // Delete first change.
@@ -600,7 +600,7 @@
             + changeResult.getCommit().name()
             + " which cannot be merged."
             + " Is the change of this commit not visible to '"
-            + admin.username
+            + admin.username()
             + "' or was it deleted?");
 
     assertRefUpdatedEvents();
@@ -613,13 +613,13 @@
     grant(project, "refs/*", Permission.SUBMIT, false, REGISTERED_USERS);
 
     // Create a change
-    PushOneCommit change = pushFactory.create(admin.getIdent(), testRepo, "fix", "a.txt", "foo");
+    PushOneCommit change = pushFactory.create(admin.newIdent(), testRepo, "fix", "a.txt", "foo");
     PushOneCommit.Result changeResult = change.to("refs/for/master");
     approve(changeResult.getChangeId());
 
     // Create a successor change.
     PushOneCommit change2 =
-        pushFactory.create(admin.getIdent(), testRepo, "feature", "b.txt", "bar");
+        pushFactory.create(admin.newIdent(), testRepo, "feature", "b.txt", "bar");
     PushOneCommit.Result change2Result = change2.to("refs/for/master");
 
     // Move the first change to a destination branch that is non-visible to user so that user cannot
@@ -632,7 +632,7 @@
     gApi.changes().id(changeResult.getChangeId()).move(secretBranch.get());
     block(secretBranch.get(), "read", ANONYMOUS_USERS);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     // Verify that user cannot see the first change.
     try {
@@ -655,7 +655,7 @@
             + changeResult.getCommit().name()
             + " which cannot be merged."
             + " Is the change of this commit not visible to '"
-            + user.username
+            + user.username()
             + "' or was it deleted?");
 
     assertRefUpdatedEvents();
@@ -668,20 +668,20 @@
     grant(project, "refs/*", Permission.SUBMIT, false, REGISTERED_USERS);
 
     // Create a change
-    PushOneCommit change = pushFactory.create(admin.getIdent(), testRepo, "fix", "a.txt", "foo");
+    PushOneCommit change = pushFactory.create(admin.newIdent(), testRepo, "fix", "a.txt", "foo");
     PushOneCommit.Result changeResult = change.to("refs/for/master");
     approve(changeResult.getChangeId());
 
     // Create a successor change.
     PushOneCommit change2 =
-        pushFactory.create(admin.getIdent(), testRepo, "feature", "b.txt", "bar");
+        pushFactory.create(admin.newIdent(), testRepo, "feature", "b.txt", "bar");
     PushOneCommit.Result change2Result = change2.to("refs/for/master");
     approve(change2Result.getChangeId());
 
     // Mark the first change private so that it's not visible to user.
     gApi.changes().id(changeResult.getChangeId()).setPrivate(true, "nobody should see this");
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     // Verify that user cannot see the first change.
     try {
@@ -730,7 +730,7 @@
         createChange(repo1, "master", "A fresh change in repo1", "a.txt", "1", "topic-to-submit");
     approve(change1.getChangeId());
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), repo2, "An ancestor change in repo2", "a.txt", "2");
+        pushFactory.create(admin.newIdent(), repo2, "An ancestor change in repo2", "a.txt", "2");
     PushOneCommit.Result change2a = push.to("refs/for/master");
     approve(change2a.getChangeId());
     PushOneCommit.Result change2b =
@@ -741,7 +741,7 @@
     // Mark change2a private so that it's not visible to user.
     gApi.changes().id(change2a.getChangeId()).setPrivate(true, "nobody should see this");
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     // Verify that user cannot see change2a
     try {
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
index d9a26aa..eb8dea5 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseAlwaysIT.java
@@ -64,8 +64,8 @@
     assertCurrentRevision(change.getChangeId(), 2, head);
     assertSubmitter(change.getChangeId(), 1);
     assertSubmitter(change.getChangeId(), 2);
-    assertPersonEquals(admin.getIdent(), head.getAuthorIdent());
-    assertPersonEquals(admin.getIdent(), head.getCommitterIdent());
+    assertPersonEquals(admin.newIdent(), head.getAuthorIdent());
+    assertPersonEquals(admin.newIdent(), head.getCommitterIdent());
     assertRefUpdatedEvents(oldHead, head);
     assertChangeMergedEvents(change.getChangeId(), head.name());
   }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java
index 19f1706..7bb31a0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitByRebaseIfNecessaryIT.java
@@ -42,8 +42,8 @@
     assertApproved(change.getChangeId());
     assertCurrentRevision(change.getChangeId(), 1, head);
     assertSubmitter(change.getChangeId(), 1);
-    assertPersonEquals(admin.getIdent(), head.getAuthorIdent());
-    assertPersonEquals(admin.getIdent(), head.getCommitterIdent());
+    assertPersonEquals(admin.newIdent(), head.getAuthorIdent());
+    assertPersonEquals(admin.newIdent(), head.getCommitterIdent());
     assertRefUpdatedEvents(oldHead, head);
     assertChangeMergedEvents(change.getChangeId(), head.name());
   }
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
index b4455f7..87fc9f6 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
@@ -29,7 +29,6 @@
 import com.google.gerrit.server.submit.ChangeSet;
 import com.google.gerrit.server.submit.MergeSuperSet;
 import com.google.gerrit.testing.ConfigSuite;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -305,7 +304,7 @@
   }
 
   private void assertChangeSetMergeable(ChangeData change, boolean expected)
-      throws MissingObjectException, IncorrectObjectTypeException, IOException, OrmException,
+      throws MissingObjectException, IncorrectObjectTypeException, IOException,
           PermissionBackendException {
     ChangeSet cs = mergeSuperSet.get().completeChangeSet(change.change(), user(admin));
     assertThat(submit.unmergeableChanges(cs).isEmpty()).isEqualTo(expected);
@@ -333,7 +332,7 @@
       List<RevCommit> parents,
       String ref)
       throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), repo, subject, fileName, content);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), repo, subject, fileName, content);
 
     if (!parents.isEmpty()) {
       push.setParents(parents);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
index 13321d0..9bbe1dd 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/SuggestReviewersIT.java
@@ -66,9 +66,11 @@
     user3 = user("user3", "First3 Last3");
     user4 = user("jdoe", "John Doe", "JDOE");
 
-    group1 = groupOperations.newGroup().name(name("users1")).members(user1.id, user3.id).create();
-    group2 = groupOperations.newGroup().name(name("users2")).members(user2.id, user3.id).create();
-    group3 = groupOperations.newGroup().name(name("users3")).members(user1.id).create();
+    group1 =
+        groupOperations.newGroup().name(name("users1")).members(user1.id(), user3.id()).create();
+    group2 =
+        groupOperations.newGroup().name(name("users2")).members(user2.id(), user3.id()).create();
+    group3 = groupOperations.newGroup().name(name("users3")).members(user1.id()).create();
   }
 
   @Test
@@ -122,7 +124,9 @@
     assertThat(reviewers.get(0).account).isNotNull();
     assertThat(ImmutableList.of(reviewers.get(0).account._accountId))
         .containsAnyIn(
-            ImmutableList.of(user1, user2, user3).stream().map(u -> u.id.get()).collect(toList()));
+            ImmutableList.of(user1, user2, user3).stream()
+                .map(u -> u.id().get())
+                .collect(toList()));
   }
 
   @Test
@@ -131,23 +135,23 @@
     String changeId = createChange().getChangeId();
     List<SuggestedReviewerInfo> reviewers;
 
-    reviewers = suggestReviewers(changeId, user2.username, 2);
+    reviewers = suggestReviewers(changeId, user2.username(), 2);
     assertThat(reviewers).hasSize(1);
-    assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName);
+    assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName());
 
-    requestScopeOperations.setApiUser(user1.getId());
-    reviewers = suggestReviewers(changeId, user2.fullName, 2);
+    requestScopeOperations.setApiUser(user1.id());
+    reviewers = suggestReviewers(changeId, user2.fullName(), 2);
     assertThat(reviewers).isEmpty();
 
-    requestScopeOperations.setApiUser(user2.getId());
-    reviewers = suggestReviewers(changeId, user2.username, 2);
+    requestScopeOperations.setApiUser(user2.id());
+    reviewers = suggestReviewers(changeId, user2.username(), 2);
     assertThat(reviewers).hasSize(1);
-    assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName);
+    assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName());
 
-    requestScopeOperations.setApiUser(user3.getId());
-    reviewers = suggestReviewers(changeId, user2.username, 2);
+    requestScopeOperations.setApiUser(user3.id());
+    reviewers = suggestReviewers(changeId, user2.username(), 2);
     assertThat(reviewers).hasSize(1);
-    assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName);
+    assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName());
   }
 
   @Test
@@ -155,10 +159,10 @@
     String changeId = createChange().getChangeId();
     List<SuggestedReviewerInfo> reviewers;
 
-    requestScopeOperations.setApiUser(user3.getId());
+    requestScopeOperations.setApiUser(user3.id());
     block("refs/*", "read", ANONYMOUS_USERS);
     allow("refs/*", "read", group1);
-    reviewers = suggestReviewers(changeId, user2.username, 2);
+    reviewers = suggestReviewers(changeId, user2.username(), 2);
     assertThat(reviewers).isEmpty();
   }
 
@@ -168,16 +172,16 @@
     String changeId = createChange().getChangeId();
     List<SuggestedReviewerInfo> reviewers;
 
-    requestScopeOperations.setApiUser(user1.getId());
-    reviewers = suggestReviewers(changeId, user2.username, 2);
+    requestScopeOperations.setApiUser(user1.id());
+    reviewers = suggestReviewers(changeId, user2.username(), 2);
     assertThat(reviewers).isEmpty();
 
     // Clear cached group info.
-    requestScopeOperations.setApiUser(user1.getId());
+    requestScopeOperations.setApiUser(user1.id());
     allowGlobalCapabilities(group1, GlobalCapability.VIEW_ALL_ACCOUNTS);
-    reviewers = suggestReviewers(changeId, user2.username, 2);
+    reviewers = suggestReviewers(changeId, user2.username(), 2);
     assertThat(reviewers).hasSize(1);
-    assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName);
+    assertThat(Iterables.getOnlyElement(reviewers).account.name).isEqualTo(user2.fullName());
   }
 
   @Test
@@ -223,27 +227,27 @@
     reviewers = suggestReviewers(changeId, name("user"));
     assertThat(reviewers).hasSize(6);
 
-    reviewers = suggestReviewers(changeId, user1.username);
+    reviewers = suggestReviewers(changeId, user1.username());
     assertThat(reviewers).hasSize(1);
 
     reviewers = suggestReviewers(changeId, "example.com");
     assertThat(reviewers).hasSize(5);
 
-    reviewers = suggestReviewers(changeId, user1.email);
+    reviewers = suggestReviewers(changeId, user1.email());
     assertThat(reviewers).hasSize(1);
 
-    reviewers = suggestReviewers(changeId, user1.username + " example");
+    reviewers = suggestReviewers(changeId, user1.username() + " example");
     assertThat(reviewers).hasSize(1);
 
-    reviewers = suggestReviewers(changeId, user4.email.toLowerCase());
+    reviewers = suggestReviewers(changeId, user4.email().toLowerCase());
     assertThat(reviewers).hasSize(1);
-    assertThat(reviewers.get(0).account.email).isEqualTo(user4.email);
+    assertThat(reviewers.get(0).account.email).isEqualTo(user4.email());
   }
 
   @Test
   public void suggestReviewersWithoutLimitOptionSpecified() throws Exception {
     String changeId = createChange().getChangeId();
-    String query = user3.username;
+    String query = user3.username();
     List<SuggestedReviewerInfo> suggestedReviewerInfos =
         gApi.changes().id(changeId).suggestReviewers(query).get();
     assertThat(suggestedReviewerInfos).hasSize(1);
@@ -333,40 +337,40 @@
     TestAccount reviewer1 = user("customuser2", "User2");
     TestAccount reviewer2 = user("customuser3", "User3");
 
-    requestScopeOperations.setApiUser(user1.getId());
+    requestScopeOperations.setApiUser(user1.id());
     String changeId1 = createChangeFromApi();
 
-    requestScopeOperations.setApiUser(reviewer1.getId());
+    requestScopeOperations.setApiUser(reviewer1.id());
     reviewChange(changeId1);
 
-    requestScopeOperations.setApiUser(user1.getId());
+    requestScopeOperations.setApiUser(user1.id());
     String changeId2 = createChangeFromApi();
 
-    requestScopeOperations.setApiUser(reviewer1.getId());
+    requestScopeOperations.setApiUser(reviewer1.id());
     reviewChange(changeId2);
 
-    requestScopeOperations.setApiUser(reviewer2.getId());
+    requestScopeOperations.setApiUser(reviewer2.id());
     reviewChange(changeId2);
 
-    requestScopeOperations.setApiUser(user1.getId());
+    requestScopeOperations.setApiUser(user1.id());
     String changeId3 = createChangeFromApi();
     List<SuggestedReviewerInfo> reviewers = suggestReviewers(changeId3, null, 4);
     assertThat(reviewers.stream().map(r -> r.account._accountId).collect(toList()))
-        .containsExactly(reviewer1.id.get(), reviewer2.id.get())
+        .containsExactly(reviewer1.id().get(), reviewer2.id().get())
         .inOrder();
 
     // check that existing reviewers are filtered out
-    gApi.changes().id(changeId3).addReviewer(reviewer1.email);
+    gApi.changes().id(changeId3).addReviewer(reviewer1.email());
     reviewers = suggestReviewers(changeId3, null, 4);
     assertThat(reviewers.stream().map(r -> r.account._accountId).collect(toList()))
-        .containsExactly(reviewer2.id.get())
+        .containsExactly(reviewer2.id().get())
         .inOrder();
   }
 
   @Test
   public void defaultReviewerSuggestionOnFirstChange() throws Exception {
     TestAccount user1 = user("customuser1", "User1");
-    requestScopeOperations.setApiUser(user1.getId());
+    requestScopeOperations.setApiUser(user1.id());
     List<SuggestedReviewerInfo> reviewers = suggestReviewers(createChange().getChangeId(), "", 4);
     assertThat(reviewers).isEmpty();
   }
@@ -385,23 +389,23 @@
     TestAccount userWhoLooksForSuggestions = user("customuser5", fullName);
 
     // Create a change as userWhoOwns and add some reviews
-    requestScopeOperations.setApiUser(userWhoOwns.getId());
+    requestScopeOperations.setApiUser(userWhoOwns.id());
     String changeId1 = createChangeFromApi();
 
-    requestScopeOperations.setApiUser(reviewer1.getId());
+    requestScopeOperations.setApiUser(reviewer1.id());
     reviewChange(changeId1);
 
-    requestScopeOperations.setApiUser(user1.getId());
+    requestScopeOperations.setApiUser(user1.id());
     String changeId2 = createChangeFromApi();
 
-    requestScopeOperations.setApiUser(reviewer1.getId());
+    requestScopeOperations.setApiUser(reviewer1.id());
     reviewChange(changeId2);
 
-    requestScopeOperations.setApiUser(reviewer2.getId());
+    requestScopeOperations.setApiUser(reviewer2.id());
     reviewChange(changeId2);
 
     // Create a comment as a different user
-    requestScopeOperations.setApiUser(userWhoComments.getId());
+    requestScopeOperations.setApiUser(userWhoComments.id());
     ReviewInput ri = new ReviewInput();
     ri.message = "Test";
     gApi.changes().id(changeId1).revision(1).review(ri);
@@ -409,11 +413,14 @@
     // Create a change as a new user to assert that we receive the correct
     // ranking
 
-    requestScopeOperations.setApiUser(userWhoLooksForSuggestions.getId());
+    requestScopeOperations.setApiUser(userWhoLooksForSuggestions.id());
     List<SuggestedReviewerInfo> reviewers = suggestReviewers(createChangeFromApi(), "Pri", 4);
     assertThat(reviewers.stream().map(r -> r.account._accountId).collect(toList()))
         .containsExactly(
-            reviewer1.id.get(), reviewer2.id.get(), userWhoOwns.id.get(), userWhoComments.id.get())
+            reviewer1.id().get(),
+            reviewer2.id().get(),
+            userWhoOwns.id().get(),
+            userWhoComments.id().get())
         .inOrder();
   }
 
@@ -428,31 +435,31 @@
     TestAccount reviewer1 = user("customuser2", fullName);
     TestAccount reviewer2 = user("customuser3", fullName);
 
-    requestScopeOperations.setApiUser(userWhoOwns.getId());
+    requestScopeOperations.setApiUser(userWhoOwns.id());
     String changeId1 = createChangeFromApi();
 
-    requestScopeOperations.setApiUser(reviewer1.getId());
+    requestScopeOperations.setApiUser(reviewer1.id());
     reviewChange(changeId1);
 
-    requestScopeOperations.setApiUser(userWhoOwns.getId());
+    requestScopeOperations.setApiUser(userWhoOwns.id());
     String changeId2 = createChangeFromApi(newProject);
 
-    requestScopeOperations.setApiUser(reviewer2.getId());
+    requestScopeOperations.setApiUser(reviewer2.id());
     reviewChange(changeId2);
 
-    requestScopeOperations.setApiUser(userWhoOwns.getId());
+    requestScopeOperations.setApiUser(userWhoOwns.id());
     String changeId3 = createChangeFromApi(newProject);
 
-    requestScopeOperations.setApiUser(reviewer2.getId());
+    requestScopeOperations.setApiUser(reviewer2.id());
     reviewChange(changeId3);
 
-    requestScopeOperations.setApiUser(userWhoOwns.getId());
+    requestScopeOperations.setApiUser(userWhoOwns.id());
     List<SuggestedReviewerInfo> reviewers = suggestReviewers(createChangeFromApi(), "Prim", 4);
 
     // Assert that reviewer1 is on top, even though reviewer2 has more reviews
     // in other projects
     assertThat(reviewers.stream().map(r -> r.account._accountId).collect(toList()))
-        .containsExactly(reviewer1.id.get(), reviewer2.id.get())
+        .containsExactly(reviewer1.id().get(), reviewer2.id().get())
         .inOrder();
   }
 
@@ -460,17 +467,17 @@
   public void suggestNoInactiveAccounts() throws Exception {
     String name = name("foo");
     TestAccount foo1 = accountCreator.create(name + "-1");
-    assertThat(gApi.accounts().id(foo1.username).getActive()).isTrue();
+    assertThat(gApi.accounts().id(foo1.username()).getActive()).isTrue();
 
     TestAccount foo2 = accountCreator.create(name + "-2");
-    assertThat(gApi.accounts().id(foo2.username).getActive()).isTrue();
+    assertThat(gApi.accounts().id(foo2.username()).getActive()).isTrue();
 
     String changeId = createChange().getChangeId();
     assertReviewers(
         suggestReviewers(changeId, name), ImmutableList.of(foo1, foo2), ImmutableList.of());
 
-    gApi.accounts().id(foo2.username).setActive(false);
-    assertThat(gApi.accounts().id(foo2.id.get()).getActive()).isFalse();
+    gApi.accounts().id(foo2.username()).setActive(false);
+    assertThat(gApi.accounts().id(foo2.id().get()).getActive()).isFalse();
     assertReviewers(suggestReviewers(changeId, name), ImmutableList.of(foo1), ImmutableList.of());
   }
 
@@ -492,7 +499,7 @@
     String secondaryEmail = "foo.secondary@example.com";
     createAccountWithSecondaryEmail("foo", secondaryEmail);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     List<SuggestedReviewerInfo> reviewers =
         suggestReviewers(createChange().getChangeId(), secondaryEmail, 4);
     assertThat(reviewers).isEmpty();
@@ -512,7 +519,7 @@
     assertThat(Iterables.getOnlyElement(reviewers).account.secondaryEmails)
         .containsExactly(secondaryEmail);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     reviewers = suggestReviewers(createChange().getChangeId(), "foo", 4);
     assertReviewers(reviewers, ImmutableList.of(foo), ImmutableList.of());
     assertThat(Iterables.getOnlyElement(reviewers).account.secondaryEmails).isNull();
@@ -524,7 +531,7 @@
     EmailInput input = new EmailInput();
     input.email = secondaryEmail;
     input.noConfirmation = true;
-    gApi.accounts().id(foo.id.get()).addEmail(input);
+    gApi.accounts().id(foo.id().get()).addEmail(input);
     return foo;
   }
 
@@ -582,7 +589,7 @@
             .map(i -> i.account._accountId)
             .collect(toList());
     assertThat(actualAccountIds)
-        .containsExactlyElementsIn(expectedUsers.stream().map(u -> u.id.get()).collect(toList()));
+        .containsExactlyElementsIn(expectedUsers.stream().map(u -> u.id().get()).collect(toList()));
 
     List<String> actualGroupIds =
         actual.stream().filter(i -> i.group != null).map(i -> i.group.id).collect(toList());
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/WorkInProgressByDefaultIT.java b/javatests/com/google/gerrit/acceptance/rest/change/WorkInProgressByDefaultIT.java
index 259c4d2..49692dd 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/WorkInProgressByDefaultIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/WorkInProgressByDefaultIT.java
@@ -48,10 +48,10 @@
 
   @After
   public void tearDown() throws Exception {
-    requestScopeOperations.setApiUser(admin.getId());
-    GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id.get()).getPreferences();
+    requestScopeOperations.setApiUser(admin.id());
+    GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id().get()).getPreferences();
     prefs.workInProgressByDefault = false;
-    gApi.accounts().id(admin.id.get()).setPreferences(prefs);
+    gApi.accounts().id(admin.id().get()).setPreferences(prefs);
   }
 
   @Test
@@ -145,9 +145,9 @@
   }
 
   private void setWorkInProgressByDefaultForUser() throws Exception {
-    GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id.get()).getPreferences();
+    GeneralPreferencesInfo prefs = gApi.accounts().id(admin.id().get()).getPreferences();
     prefs.workInProgressByDefault = true;
-    gApi.accounts().id(admin.id.get()).setPreferences(prefs);
+    gApi.accounts().id(admin.id().get()).setPreferences(prefs);
   }
 
   private PushOneCommit.Result createChange(Project.NameKey p) throws Exception {
@@ -156,7 +156,7 @@
 
   private PushOneCommit.Result createChange(Project.NameKey p, String r) throws Exception {
     TestRepository<InMemoryRepository> testRepo = cloneProject(p);
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result result = push.to(r);
     result.assertOkStatus();
     return result;
diff --git a/javatests/com/google/gerrit/acceptance/rest/config/ConfirmEmailIT.java b/javatests/com/google/gerrit/acceptance/rest/config/ConfirmEmailIT.java
index 3f76a27..99fdbc8 100644
--- a/javatests/com/google/gerrit/acceptance/rest/config/ConfirmEmailIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/config/ConfirmEmailIT.java
@@ -36,14 +36,14 @@
   @Test
   public void confirm() throws Exception {
     ConfirmEmail.Input in = new ConfirmEmail.Input();
-    in.token = emailTokenVerifier.encode(admin.getId(), "new.mail@example.com");
+    in.token = emailTokenVerifier.encode(admin.id(), "new.mail@example.com");
     adminRestSession.put("/config/server/email.confirm", in).assertNoContent();
   }
 
   @Test
   public void confirmForOtherUser_UnprocessableEntity() throws Exception {
     ConfirmEmail.Input in = new ConfirmEmail.Input();
-    in.token = emailTokenVerifier.encode(user.getId(), "new.mail@example.com");
+    in.token = emailTokenVerifier.encode(user.id(), "new.mail@example.com");
     adminRestSession.put("/config/server/email.confirm", in).assertUnprocessableEntity();
   }
 
@@ -57,7 +57,7 @@
   @Test
   public void confirmAlreadyInUse_UnprocessableEntity() throws Exception {
     ConfirmEmail.Input in = new ConfirmEmail.Input();
-    in.token = emailTokenVerifier.encode(admin.getId(), user.email);
+    in.token = emailTokenVerifier.encode(admin.id(), user.email());
     adminRestSession.put("/config/server/email.confirm", in).assertUnprocessableEntity();
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/AbstractPushTag.java b/javatests/com/google/gerrit/acceptance/rest/project/AbstractPushTag.java
index 95bc5a6..3d987a3 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/AbstractPushTag.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/AbstractPushTag.java
@@ -188,7 +188,7 @@
     if (force) {
       testRepo.reset(initialHead);
     }
-    commit(user.getIdent(), "subject");
+    commit(user.newIdent(), "subject");
 
     boolean createTag = tagName == null;
     tagName = MoreObjects.firstNonNull(tagName, "v1_" + System.nanoTime());
@@ -197,9 +197,9 @@
         break;
       case ANNOTATED:
         if (createTag) {
-          createAnnotatedTag(testRepo, tagName, user.getIdent());
+          createAnnotatedTag(testRepo, tagName, user.newIdent());
         } else {
-          updateAnnotatedTag(testRepo, tagName, user.getIdent());
+          updateAnnotatedTag(testRepo, tagName, user.newIdent());
         }
         break;
       default:
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java b/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
index d1a80c7..72af075 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/AccessIT.java
@@ -171,7 +171,7 @@
   public void createAccessChange() throws Exception {
     allow(newProjectName, RefNames.REFS_CONFIG, Permission.READ, REGISTERED_USERS);
     // User can see the branch
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     pApi().branch("refs/heads/master").get();
 
     ProjectAccessInput accessInput = newProjectAccessInput();
@@ -186,7 +186,7 @@
     accessSection.permissions.put(Permission.READ, read);
     accessInput.add.put(REFS_HEADS, accessSection);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     ChangeInfo out = pApi().accessChange(accessInput);
 
     assertThat(out.project).isEqualTo(newProjectName.get());
@@ -194,7 +194,7 @@
     assertThat(out.status).isEqualTo(ChangeStatus.NEW);
     assertThat(out.submitted).isNull();
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
 
     ChangeInfo c = gApi.changes().id(out._number).get(MESSAGES);
     assertThat(c.messages.stream().map(m -> m.message)).containsExactly("Uploaded patch set 1");
@@ -205,7 +205,7 @@
     gApi.changes().id(out._number).current().submit();
 
     // check that the change took effect.
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     try {
       BranchInfo info = pApi().branch("refs/heads/master").get();
       fail("wanted failure, got " + newGson().toJson(info));
@@ -216,16 +216,16 @@
     // Restore.
     accessInput.add.clear();
     accessInput.remove.put(REFS_HEADS, accessSection);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     out = pApi().accessChange(accessInput);
 
     gApi.changes().id(out._number).current().review(reviewIn);
     gApi.changes().id(out._number).current().submit();
 
     // Now it works again.
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     pApi().branch("refs/heads/master").get();
   }
 
@@ -325,7 +325,7 @@
     accessInput.add.put(REFS_ALL, accessSectionInfo);
     pApi().access(accessInput);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(ResourceNotFoundException.class);
     pApi().access();
   }
@@ -345,7 +345,7 @@
     AccessSectionInfo accessSectionInfoToApply = createDefaultAccessSectionInfo();
     accessInfoToApply.add.put(REFS_HEADS, accessSectionInfoToApply);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(ResourceNotFoundException.class);
     pApi().access();
   }
@@ -408,7 +408,7 @@
     ProjectAccessInput accessInput = newProjectAccessInput();
     accessInput.parent = newParentProjectName;
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     exception.expectMessage("administrate server not permitted");
     pApi().access(accessInput);
@@ -435,7 +435,7 @@
 
     accessInput.add.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     gApi.projects().name(allProjects.get()).access(accessInput);
   }
@@ -455,7 +455,7 @@
                 .get(AccessSection.GLOBAL_CAPABILITIES)
                 .permissions
                 .keySet())
-        .containsAllIn(accessSectionInfo.permissions.keySet());
+        .containsAtLeastElementsIn(accessSectionInfo.permissions.keySet());
   }
 
   @Test
@@ -491,7 +491,7 @@
 
     accessInput.remove.put(AccessSection.GLOBAL_CAPABILITIES, accessSectionInfo);
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     gApi.projects().name(allProjects.get()).access(accessInput);
   }
@@ -517,7 +517,7 @@
                 .get(AccessSection.GLOBAL_CAPABILITIES)
                 .permissions
                 .keySet())
-        .containsAllIn(accessSectionInfo.permissions.keySet());
+        .containsAtLeastElementsIn(accessSectionInfo.permissions.keySet());
 
     // Remove
     accessInput.add.clear();
@@ -561,7 +561,7 @@
     config = cfg.toText();
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(), allProjectsRepo, "Subject", ProjectConfig.PROJECT_CONFIG, config);
+            admin.newIdent(), allProjectsRepo, "Subject", ProjectConfig.PROJECT_CONFIG, config);
     push.to(RefNames.REFS_CONFIG).assertOkStatus();
 
     // Verify that unknownPermission is present
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/BUILD b/javatests/com/google/gerrit/acceptance/rest/project/BUILD
index 7af7d04..131c24a 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/BUILD
+++ b/javatests/com/google/gerrit/acceptance/rest/project/BUILD
@@ -34,7 +34,6 @@
         "//java/com/google/gerrit/reviewdb:server",
         "//java/com/google/gerrit/server",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib/truth",
     ],
 )
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
index 8943125..c06ec69 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/CreateBranchIT.java
@@ -63,7 +63,7 @@
 
   @Test
   public void createBranch_Forbidden() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertCreateFails(testBranch, AuthException.class, "not permitted: create on refs/heads/test");
   }
 
@@ -81,7 +81,7 @@
   @Test
   public void createBranchByProjectOwner() throws Exception {
     grantOwner();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertCreateSucceeds(testBranch);
   }
 
@@ -95,7 +95,7 @@
   public void createBranchByProjectOwnerCreateReferenceBlocked_Forbidden() throws Exception {
     grantOwner();
     blockCreateReference();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertCreateFails(testBranch, AuthException.class, "not permitted: create on refs/heads/test");
   }
 
@@ -113,7 +113,7 @@
     allow(allUsers, RefNames.REFS_USERS + "*", Permission.PUSH, REGISTERED_USERS);
     assertCreateFails(
         new Branch.NameKey(allUsers, RefNames.refsUsers(new Account.Id(1))),
-        RefNames.refsUsers(admin.getId()),
+        RefNames.refsUsers(admin.id()),
         ResourceConflictException.class,
         "Not allowed to create user branch.");
   }
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
index 7d36e71..d8fbbe5 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
@@ -121,7 +121,7 @@
         Future<RestResponse> r1 = executor.submit(createProjectFoo);
         Future<RestResponse> r2 = executor.submit(createProjectFoo);
         assertThat(ImmutableList.of(r1.get().getStatusCode(), r2.get().getStatusCode()))
-            .containsAllOf(HttpStatus.SC_CREATED, HttpStatus.SC_CONFLICT);
+            .containsAtLeast(HttpStatus.SC_CREATED, HttpStatus.SC_CONFLICT);
       }
     } finally {
       executor.shutdown();
@@ -325,7 +325,7 @@
   public void createProjectWithCapability() throws Exception {
     allowGlobalCapabilities(SystemGroupBackend.REGISTERED_USERS, GlobalCapability.CREATE_PROJECT);
     try {
-      requestScopeOperations.setApiUser(user.getId());
+      requestScopeOperations.setApiUser(user.id());
       ProjectInput in = new ProjectInput();
       in.name = name("newProject");
       ProjectInfo p = gApi.projects().create(in).get();
@@ -338,7 +338,7 @@
 
   @Test
   public void createProjectWithoutCapability_Forbidden() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     ProjectInput in = new ProjectInput();
     in.name = name("newProject");
     assertCreateFails(in, AuthException.class);
@@ -357,7 +357,7 @@
     parent.setState(com.google.gerrit.extensions.client.ProjectState.HIDDEN);
     allowGlobalCapabilities(SystemGroupBackend.REGISTERED_USERS, GlobalCapability.CREATE_PROJECT);
     try {
-      requestScopeOperations.setApiUser(user.getId());
+      requestScopeOperations.setApiUser(user.id());
       ProjectInput in = new ProjectInput();
       in.name = name("newProject");
       ProjectInfo p = gApi.projects().create(in).get();
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java b/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java
index 8ca6b75..8c96662 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchIT.java
@@ -51,7 +51,7 @@
 
   @Test
   public void deleteBranch_Forbidden() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertDeleteForbidden(testBranch);
   }
 
@@ -63,7 +63,7 @@
   @Test
   public void deleteBranchByProjectOwner() throws Exception {
     grantOwner();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertDeleteSucceeds(testBranch);
   }
 
@@ -77,21 +77,21 @@
   public void deleteBranchByProjectOwnerForcePushBlocked_Forbidden() throws Exception {
     grantOwner();
     blockForcePush();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertDeleteForbidden(testBranch);
   }
 
   @Test
   public void deleteBranchByUserWithForcePushPermission() throws Exception {
     grantForcePush();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertDeleteSucceeds(testBranch);
   }
 
   @Test
   public void deleteBranchByUserWithDeletePermission() throws Exception {
     grantDelete();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertDeleteSucceeds(testBranch);
   }
 
@@ -138,7 +138,7 @@
 
     exception.expect(ResourceConflictException.class);
     exception.expectMessage("Not allowed to delete user branch.");
-    branch(new Branch.NameKey(allUsers, RefNames.refsUsers(admin.id))).delete();
+    branch(new Branch.NameKey(allUsers, RefNames.refsUsers(admin.id()))).delete();
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchesIT.java b/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchesIT.java
index a85004e..47b6a78 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/DeleteBranchesIT.java
@@ -74,14 +74,14 @@
 
     DeleteBranchesInput input = new DeleteBranchesInput();
     input.branches = branchToDelete;
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     try {
       project().deleteBranches(input);
       fail("Expected AuthException");
     } catch (AuthException e) {
       assertThat(e).hasMessageThat().isEqualTo("not permitted: delete on refs/heads/test-1");
     }
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     assertBranches(BRANCHES);
   }
 
@@ -89,14 +89,14 @@
   public void deleteMultiBranchesWithoutPermissionForbidden() throws Exception {
     DeleteBranchesInput input = new DeleteBranchesInput();
     input.branches = BRANCHES;
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     try {
       project().deleteBranches(input);
       fail("Expected ResourceConflictException");
     } catch (ResourceConflictException e) {
       assertThat(e).hasMessageThat().isEqualTo(errorMessageForBranches(BRANCHES));
     }
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     assertBranches(BRANCHES);
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagIT.java b/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagIT.java
index 27de4b1..07bb2b1 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagIT.java
@@ -44,7 +44,7 @@
 
   @Test
   public void deleteTag_Forbidden() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertDeleteForbidden();
   }
 
@@ -56,7 +56,7 @@
   @Test
   public void deleteTagByProjectOwner() throws Exception {
     grantOwner();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertDeleteSucceeds();
   }
 
@@ -70,21 +70,21 @@
   public void deleteTagByProjectOwnerForcePushBlocked_Forbidden() throws Exception {
     grantOwner();
     blockForcePush();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertDeleteForbidden();
   }
 
   @Test
   public void deleteTagByUserWithForcePushPermission() throws Exception {
     grantForcePush();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertDeleteSucceeds();
   }
 
   @Test
   public void deleteTagByUserWithDeletePermission() throws Exception {
     grantDelete();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertDeleteSucceeds();
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagsIT.java b/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagsIT.java
index 6d8689a..fae9d00 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/DeleteTagsIT.java
@@ -65,14 +65,14 @@
   public void deleteTagsForbidden() throws Exception {
     DeleteTagsInput input = new DeleteTagsInput();
     input.tags = TAGS;
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     try {
       project().deleteTags(input);
       fail("Expected ResourceConflictException");
     } catch (ResourceConflictException e) {
       assertThat(e).hasMessageThat().isEqualTo(errorMessageForTags(TAGS));
     }
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     assertTags(TAGS);
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java b/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java
index 1963455..18c706b 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/GetCommitIT.java
@@ -85,7 +85,7 @@
   @Test
   public void getOpenChange_Found() throws Exception {
     unblockRead();
-    PushOneCommit.Result r = pushFactory.create(admin.getIdent(), testRepo).to("refs/for/master");
+    PushOneCommit.Result r = pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master");
     r.assertOkStatus();
 
     CommitInfo info = getCommit(r.getCommit());
@@ -107,7 +107,7 @@
 
   @Test
   public void getOpenChange_NotFound() throws Exception {
-    PushOneCommit.Result r = pushFactory.create(admin.getIdent(), testRepo).to("refs/for/master");
+    PushOneCommit.Result r = pushFactory.create(admin.newIdent(), testRepo).to("refs/for/master");
     r.assertOkStatus();
     assertNotFound(r.getCommit());
   }
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java b/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
index 7f2b35e..ec1c708 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ListBranchesIT.java
@@ -42,7 +42,7 @@
   @Test
   public void listBranchesOfNonVisibleProject_NotFound() throws Exception {
     blockRead("refs/*");
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(ResourceNotFoundException.class);
     gApi.projects().name(project.get()).branches().get();
   }
@@ -74,7 +74,7 @@
     blockRead("refs/heads/dev");
     String master = pushTo("refs/heads/master").getCommit().name();
     pushTo("refs/heads/dev");
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     // refs/meta/config is hidden since user is no project owner
     assertRefs(
         ImmutableList.of(
@@ -87,7 +87,7 @@
     blockRead("refs/heads/master");
     pushTo("refs/heads/master");
     String dev = pushTo("refs/heads/dev").getCommit().name();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     // refs/meta/config is hidden since user is no project owner
     assertRefs(ImmutableList.of(branch("refs/heads/dev", dev, false)), list().get());
   }
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java b/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
index f2c369e..d9b8e03 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
@@ -69,7 +69,7 @@
 
   @Test
   public void listProjectsFiltersInvisibleProjects() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     assertThatNameList(gApi.projects().list().get()).contains(project);
 
     try (ProjectConfigUpdate u = updateProject(project)) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java b/javatests/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java
index 9088afa..bf2a534 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/ProjectLevelConfigIT.java
@@ -47,7 +47,7 @@
     cfg.setString("s2", "ss", "k2", "v2");
     PushOneCommit push =
         pushFactory.create(
-            admin.getIdent(), testRepo, "Create Project Level Config", configName, cfg.toText());
+            admin.newIdent(), testRepo, "Create Project Level Config", configName, cfg.toText());
     push.to(RefNames.REFS_CONFIG);
 
     ProjectState state = projectCache.get(project);
@@ -72,7 +72,7 @@
 
     pushFactory
         .create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             "Create Project Level Config",
             configName,
@@ -91,7 +91,7 @@
 
     pushFactory
         .create(
-            admin.getIdent(),
+            admin.newIdent(),
             childTestRepo,
             "Create Project Level Config",
             configName,
@@ -125,7 +125,7 @@
 
     pushFactory
         .create(
-            admin.getIdent(),
+            admin.newIdent(),
             testRepo,
             "Create Project Level Config",
             configName,
@@ -150,7 +150,7 @@
 
     pushFactory
         .create(
-            admin.getIdent(),
+            admin.newIdent(),
             childTestRepo,
             "Create Project Level Config",
             configName,
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java b/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java
index c0f3732..0165ead 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/TagsIT.java
@@ -75,7 +75,7 @@
   @Test
   public void listTagsOfNonVisibleProject() throws Exception {
     blockRead("refs/*");
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(ResourceNotFoundException.class);
     gApi.projects().name(project.get()).tags().get();
   }
@@ -130,7 +130,7 @@
   public void listTagsOfNonVisibleBranch() throws Exception {
     grantTagPermissions();
 
-    PushOneCommit push1 = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push1 = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result r1 = push1.to("refs/heads/master");
     r1.assertOkStatus();
     TagInput tag1 = new TagInput();
@@ -141,7 +141,7 @@
     assertThat(result.revision).isEqualTo(tag1.revision);
 
     pushTo("refs/heads/hidden");
-    PushOneCommit push2 = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push2 = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result r2 = push2.to("refs/heads/hidden");
     r2.assertOkStatus();
 
@@ -170,7 +170,7 @@
   public void lightweightTag() throws Exception {
     grantTagPermissions();
 
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result r = push.to("refs/heads/master");
     r.assertOkStatus();
 
@@ -191,7 +191,7 @@
     assertThat(result.canDelete).isTrue();
     assertThat(result.created).isEqualTo(timestamp(r));
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     result = tag(input.ref).get();
     assertThat(result.canDelete).isNull();
 
@@ -202,7 +202,7 @@
   public void annotatedTag() throws Exception {
     grantTagPermissions();
 
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     PushOneCommit.Result r = push.to("refs/heads/master");
     r.assertOkStatus();
 
@@ -215,8 +215,8 @@
     assertThat(result.ref).isEqualTo(R_TAGS + input.ref);
     assertThat(result.object).isEqualTo(input.revision);
     assertThat(result.message).isEqualTo(input.message);
-    assertThat(result.tagger.name).isEqualTo(admin.fullName);
-    assertThat(result.tagger.email).isEqualTo(admin.email);
+    assertThat(result.tagger.name).isEqualTo(admin.fullName());
+    assertThat(result.tagger.email).isEqualTo(admin.email());
     assertThat(result.created).isEqualTo(result.tagger.date);
 
     eventRecorder.assertRefUpdatedEvents(project.get(), result.ref, null, result.revision);
@@ -230,8 +230,8 @@
     assertThat(result2.ref).isEqualTo(input2.ref);
     assertThat(result2.object).isEqualTo(input2.revision);
     assertThat(result2.message).isEqualTo(input2.message);
-    assertThat(result2.tagger.name).isEqualTo(admin.fullName);
-    assertThat(result2.tagger.email).isEqualTo(admin.email);
+    assertThat(result2.tagger.name).isEqualTo(admin.fullName());
+    assertThat(result2.tagger.email).isEqualTo(admin.email());
     assertThat(result2.created).isEqualTo(result2.tagger.date);
 
     eventRecorder.assertRefUpdatedEvents(project.get(), result2.ref, null, result2.revision);
diff --git a/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java b/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
index d99fa72..fa1b467 100644
--- a/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/account/AccountResolverIT.java
@@ -64,14 +64,14 @@
     accountOperations.newAccount().fullname("self").create();
 
     Result result = resolveAsResult("self");
-    assertThat(result.asIdSet()).containsExactly(admin.id);
+    assertThat(result.asIdSet()).containsExactly(admin.id());
     assertThat(result.isSelf()).isTrue();
-    assertThat(result.asUniqueUser()).isSameAs(self.get());
+    assertThat(result.asUniqueUser()).isSameInstanceAs(self.get());
 
     result = resolveAsResult("me");
-    assertThat(result.asIdSet()).containsExactly(admin.id);
+    assertThat(result.asIdSet()).containsExactly(admin.id());
     assertThat(result.isSelf()).isTrue();
-    assertThat(result.asUniqueUser()).isSameAs(self.get());
+    assertThat(result.asUniqueUser()).isSameInstanceAs(self.get());
 
     requestScopeOperations.setApiUserAnonymous();
     checkBySelfFails();
@@ -106,15 +106,15 @@
 
   @Test
   public void bySelfInactive() throws Exception {
-    gApi.accounts().id(user.id.get()).setActive(false);
+    gApi.accounts().id(user.id().get()).setActive(false);
 
-    requestScopeOperations.setApiUser(user.id);
+    requestScopeOperations.setApiUser(user.id());
     assertThat(gApi.accounts().id("self").getActive()).isFalse();
 
     Result result = resolveAsResult("self");
-    assertThat(result.asIdSet()).containsExactly(user.id);
+    assertThat(result.asIdSet()).containsExactly(user.id());
     assertThat(result.isSelf()).isTrue();
-    assertThat(result.asUniqueUser()).isSameAs(self.get());
+    assertThat(result.asUniqueUser()).isSameInstanceAs(self.get());
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
index 2a7cadc..15dd3fb 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/CommentsIT.java
@@ -86,7 +86,7 @@
 
   @Before
   public void setUp() {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
   }
 
   @Test
@@ -142,7 +142,7 @@
       String file = "file";
       String contents = "contents " + line;
       PushOneCommit push =
-          pushFactory.create(admin.getIdent(), testRepo, "first subject", file, contents);
+          pushFactory.create(admin.newIdent(), testRepo, "first subject", file, contents);
       PushOneCommit.Result r = push.to("refs/for/master");
       String changeId = r.getChangeId();
       String revId = r.getCommit().getName();
@@ -166,7 +166,7 @@
       String file = "file";
       String contents = "contents " + line;
       PushOneCommit push =
-          pushFactory.create(admin.getIdent(), testRepo, "first subject", file, contents);
+          pushFactory.create(admin.newIdent(), testRepo, "first subject", file, contents);
       PushOneCommit.Result r = push.to("refs/for/master");
       String changeId = r.getChangeId();
       String revId = r.getCommit().getName();
@@ -198,7 +198,7 @@
       String file = "file";
       String contents = "contents " + line;
       PushOneCommit push =
-          pushFactory.create(admin.getIdent(), testRepo, "first subject", file, contents);
+          pushFactory.create(admin.newIdent(), testRepo, "first subject", file, contents);
       PushOneCommit.Result r = push.to("refs/for/master");
       String changeId = r.getChangeId();
       String revId = r.getCommit().getName();
@@ -272,7 +272,7 @@
   public void listComments() throws Exception {
     String file = "file";
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), testRepo, "first subject", file, "contents");
+        pushFactory.create(admin.newIdent(), testRepo, "first subject", file, "contents");
     PushOneCommit.Result r = push.to("refs/for/master");
     String changeId = r.getChangeId();
     String revId = r.getCommit().getName();
@@ -381,7 +381,7 @@
       String file = "file";
       String contents = "contents " + line;
       PushOneCommit push =
-          pushFactory.create(admin.getIdent(), testRepo, "first subject", file, contents);
+          pushFactory.create(admin.newIdent(), testRepo, "first subject", file, contents);
       PushOneCommit.Result r = push.to("refs/for/master");
       String changeId = r.getChangeId();
       String revId = r.getCommit().getName();
@@ -424,7 +424,7 @@
 
     PushOneCommit.Result r2 =
         pushFactory
-            .create(admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "content")
+            .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "content")
             .to("refs/for/master");
     changeId = r2.getChangeId();
     revId = r2.getCommit().getName();
@@ -439,10 +439,10 @@
 
     PushOneCommit.Result r2 =
         pushFactory
-            .create(admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new content", r1.getChangeId())
+            .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "new content", r1.getChangeId())
             .to("refs/for/master");
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     addDraft(
         r1.getChangeId(),
         r1.getCommit().getName(),
@@ -452,13 +452,13 @@
         r2.getCommit().getName(),
         newDraft(FILE_NAME, Side.REVISION, 1, "typo: content"));
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     addDraft(
         r2.getChangeId(),
         r2.getCommit().getName(),
         newDraft(FILE_NAME, Side.REVISION, 1, "+1, please fix"));
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     Map<String, List<CommentInfo>> actual = gApi.changes().id(r1.getChangeId()).drafts();
     assertThat(actual.keySet()).containsExactly(FILE_NAME);
     List<CommentInfo> comments = actual.get(FILE_NAME);
@@ -485,7 +485,7 @@
 
     PushOneCommit.Result r2 =
         pushFactory
-            .create(admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "new cntent", r1.getChangeId())
+            .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "new cntent", r1.getChangeId())
             .to("refs/for/master");
 
     addComment(r1, "nit: trailing whitespace");
@@ -498,14 +498,14 @@
     assertThat(comments).hasSize(2);
 
     CommentInfo c1 = comments.get(0);
-    assertThat(c1.author._accountId).isEqualTo(user.getId().get());
+    assertThat(c1.author._accountId).isEqualTo(user.id().get());
     assertThat(c1.patchSet).isEqualTo(1);
     assertThat(c1.message).isEqualTo("nit: trailing whitespace");
     assertThat(c1.side).isNull();
     assertThat(c1.line).isEqualTo(1);
 
     CommentInfo c2 = comments.get(1);
-    assertThat(c2.author._accountId).isEqualTo(user.getId().get());
+    assertThat(c2.author._accountId).isEqualTo(user.id().get());
     assertThat(c2.patchSet).isEqualTo(2);
     assertThat(c2.message).isEqualTo("typo: content");
     assertThat(c2.side).isNull();
@@ -528,13 +528,13 @@
   public void publishCommentsAllRevisions() throws Exception {
     PushOneCommit.Result r1 =
         pushFactory
-            .create(admin.getIdent(), testRepo, SUBJECT, FILE_NAME, "old boring content\n")
+            .create(admin.newIdent(), testRepo, SUBJECT, FILE_NAME, "old boring content\n")
             .to("refs/for/master");
 
     PushOneCommit.Result r2 =
         pushFactory
             .create(
-                admin.getIdent(),
+                admin.newIdent(),
                 testRepo,
                 SUBJECT,
                 FILE_NAME,
@@ -574,11 +574,11 @@
         other.getCommit().getName(),
         newDraft(FILE_NAME, Side.REVISION, 1, "unrelated comment"));
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     // Drafts by other users aren't returned.
     addDraft(
         r2.getChangeId(), r2.getCommit().getName(), newDraft(FILE_NAME, Side.REVISION, 2, "oops"));
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     ReviewInput reviewInput = new ReviewInput();
     reviewInput.drafts = DraftHandling.PUBLISH_ALL_REVISIONS;
@@ -768,7 +768,7 @@
     String uuid = commentsMap.get(targetComment.path).get(0).id;
     DeleteCommentInput input = new DeleteCommentInput("contains confidential information");
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     gApi.changes().id(result.getChangeId()).current().comment(uuid).delete(input);
   }
@@ -839,7 +839,7 @@
     // PS4 has comments [c7, c8].
     assertThat(getRevisionComments(changeId, ps4)).hasSize(2);
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     for (int i = 0; i < commentsBeforeDelete.size(); i++) {
       List<RevCommit> commitsBeforeDelete = getChangeMetaCommitsInReverseOrder(id);
 
@@ -854,7 +854,7 @@
           gApi.changes().id(changeId).revision(patchSet).comment(uuid).delete(input);
 
       String expectedMsg =
-          String.format("Comment removed by: %s; Reason: %s", admin.fullName, input.reason);
+          String.format("Comment removed by: %s; Reason: %s", admin.fullName(), input.reason);
       assertThat(updatedComment.message).isEqualTo(expectedMsg);
       oldComment.message = expectedMsg;
       assertThat(updatedComment).isEqualTo(oldComment);
@@ -909,7 +909,7 @@
 
     List<RevCommit> commitsBeforeDelete = getChangeMetaCommitsInReverseOrder(id);
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     for (int i = 0; i < 3; i++) {
       DeleteCommentInput input = new DeleteCommentInput("delete comment 2, iteration: " + i);
       gApi.changes().id(changeId).revision(ps1).comment(uuid).delete(input);
@@ -918,7 +918,8 @@
     CommentInfo updatedComment = gApi.changes().id(changeId).revision(ps1).comment(uuid).get();
     String expectedMsg =
         String.format(
-            "Comment removed by: %s; Reason: %s", admin.fullName, "delete comment 2, iteration: 2");
+            "Comment removed by: %s; Reason: %s",
+            admin.fullName(), "delete comment 2, iteration: 2");
     assertThat(updatedComment.message).isEqualTo(expectedMsg);
     oldComment.message = expectedMsg;
     assertThat(updatedComment).isEqualTo(oldComment);
diff --git a/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java b/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
index c80cd21..7c375bd 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/ConsistencyCheckerIT.java
@@ -48,7 +48,6 @@
 import com.google.gerrit.server.update.RepoContext;
 import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.gerrit.testing.TestChanges;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import java.io.IOException;
@@ -96,7 +95,7 @@
         serverSideTestRepo
             .getRevWalk()
             .parseCommit(serverSideTestRepo.getRepository().exactRef("HEAD").getObjectId());
-    adminId = admin.getId();
+    adminId = admin.id();
     checker = checkerProvider.get();
   }
 
@@ -115,9 +114,9 @@
   public void missingOwner() throws Exception {
     TestAccount owner = accountCreator.create("missing");
     ChangeNotes notes = insertChange(owner);
-    deleteUserBranch(owner.getId());
+    deleteUserBranch(owner.id());
 
-    assertProblems(notes, null, problem("Missing change owner: " + owner.getId()));
+    assertProblems(notes, null, problem("Missing change owner: " + owner.id()));
   }
 
   // No test for ref existing but object missing; InMemoryRepository won't let
@@ -234,7 +233,7 @@
 
   @Test
   public void onlyPatchSetObjectMissingWithFix() throws Exception {
-    Change c = TestChanges.newChange(project, admin.getId(), sequences.nextChangeId());
+    Change c = TestChanges.newChange(project, admin.id(), sequences.nextChangeId());
 
     PatchSet.Id psId = c.currentPatchSetId();
     String rev = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
@@ -313,7 +312,7 @@
           notes.getChangeId(),
           new BatchUpdateOp() {
             @Override
-            public boolean updateChange(ChangeContext ctx) throws OrmException {
+            public boolean updateChange(ChangeContext ctx) {
               ctx.getChange().setStatus(Change.Status.MERGED);
               ctx.getUpdate(ctx.getChange().currentPatchSetId()).fixStatus(Change.Status.MERGED);
               return true;
@@ -747,7 +746,7 @@
   private ChangeNotes insertChange(TestAccount owner, String dest) throws Exception {
     Change.Id id = new Change.Id(sequences.nextChangeId());
     ChangeInserter ins;
-    try (BatchUpdate bu = newUpdate(owner.getId())) {
+    try (BatchUpdate bu = newUpdate(owner.id())) {
       RevCommit commit = patchSetCommit(new PatchSet.Id(id, 1));
       bu.setNotify(NotifyResolver.Result.none());
       ins =
@@ -830,8 +829,7 @@
 
   private void addNoteDbCommit(Change.Id id, String commitMessage) throws Exception {
     PersonIdent committer = serverIdent.get();
-    PersonIdent author =
-        noteUtil.newIdent(getAccount(admin.getId()), committer.getWhen(), committer);
+    PersonIdent author = noteUtil.newIdent(getAccount(admin.id()), committer.getWhen(), committer);
     serverSideTestRepo
         .branch(RefNames.changeMetaRef(id))
         .commit()
@@ -863,7 +861,7 @@
             }
 
             @Override
-            public boolean updateChange(ChangeContext ctx) throws OrmException {
+            public boolean updateChange(ChangeContext ctx) {
               ctx.getChange().setStatus(Change.Status.MERGED);
               ctx.getUpdate(ctx.getChange().currentPatchSetId()).fixStatus(Change.Status.MERGED);
               return true;
diff --git a/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java b/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
index 183cfe2..b9b7ab3 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/GetRelatedIT.java
@@ -23,6 +23,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.truth.Correspondence;
+import com.google.common.truth.Correspondence.BinaryPredicate;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.GerritConfig;
 import com.google.gerrit.acceptance.NoHttpd;
@@ -52,7 +53,6 @@
 import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.gerrit.testing.ConfigSuite;
 import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -102,7 +102,7 @@
 
   @Test
   public void getRelatedNoResult() throws Exception {
-    PushOneCommit push = pushFactory.create(admin.getIdent(), testRepo);
+    PushOneCommit push = pushFactory.create(admin.newIdent(), testRepo);
     assertRelated(push.to("refs/for/master").getPatchSetId());
   }
 
@@ -650,17 +650,15 @@
 
   private static Correspondence<RelatedChangeAndCommitInfo, String>
       getRelatedChangeToStatusCorrespondence() {
-    return new Correspondence<RelatedChangeAndCommitInfo, String>() {
-      @Override
-      public boolean compare(RelatedChangeAndCommitInfo relatedChangeAndCommitInfo, String status) {
-        return Objects.equals(relatedChangeAndCommitInfo.status, status);
-      }
-
-      @Override
-      public String toString() {
-        return "has status";
-      }
-    };
+    return Correspondence.from(
+        new BinaryPredicate<RelatedChangeAndCommitInfo, String>() {
+          @Override
+          public boolean apply(
+              RelatedChangeAndCommitInfo relatedChangeAndCommitInfo, String status) {
+            return Objects.equals(relatedChangeAndCommitInfo.status, status);
+          }
+        },
+        "has status");
   }
 
   private RevCommit parseBody(RevCommit c) throws Exception {
@@ -695,7 +693,7 @@
           psId.getParentKey(),
           new BatchUpdateOp() {
             @Override
-            public boolean updateChange(ChangeContext ctx) throws OrmException {
+            public boolean updateChange(ChangeContext ctx) {
               PatchSet ps = psUtil.get(ctx.getNotes(), psId);
               psUtil.setGroups(ctx.getUpdate(psId), ps, ImmutableList.of());
               return true;
diff --git a/javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java b/javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java
index 1892566..4e5cb5e 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java
@@ -254,7 +254,7 @@
     PatchListCacheImpl.LargeObjectTombstone tombstone =
         new PatchListCacheImpl.LargeObjectTombstone();
     abstractPatchListCache.put(key, tombstone);
-    assertThat(abstractPatchListCache.getIfPresent(key)).isSameAs(tombstone);
+    assertThat(abstractPatchListCache.getIfPresent(key)).isSameInstanceAs(tombstone);
   }
 
   private static void assertAdded(String expectedNewName, PatchListEntry e) {
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java b/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
index 79cb097..768c269 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/AbstractMailIT.java
@@ -36,8 +36,8 @@
   protected MailMessage.Builder messageBuilderWithDefaultFields() {
     MailMessage.Builder b = MailMessage.builder();
     b.id("some id");
-    b.from(user.emailAddress);
-    b.addTo(user.emailAddress); // Not evaluated
+    b.from(user.getEmailAddress());
+    b.addTo(user.getEmailAddress()); // Not evaluated
     b.subject("");
     b.dateReceived(Instant.now());
     return b;
@@ -52,12 +52,12 @@
     String file = "gerrit-server/test.txt";
     String contents = "contents \nlorem \nipsum \nlorem";
     PushOneCommit push =
-        pushFactory.create(admin.getIdent(), testRepo, "first subject", file, contents);
+        pushFactory.create(admin.newIdent(), testRepo, "first subject", file, contents);
     PushOneCommit.Result r = push.to("refs/for/master");
     String changeId = r.getChangeId();
 
     // Review it
-    requestScopeOperations.setApiUser(reviewer.getId());
+    requestScopeOperations.setApiUser(reviewer.id());
     ReviewInput input = new ReviewInput();
     input.message = "I have two comments";
     input.comments = new HashMap<>();
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java b/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
index 2845180..abf02d5 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/ChangeNotificationsIT.java
@@ -260,7 +260,7 @@
       String changeId, TestAccount by, EmailStrategy emailStrategy, @Nullable NotifyHandling notify)
       throws Exception {
     setEmailStrategy(by, emailStrategy);
-    requestScopeOperations.setApiUser(by.getId());
+    requestScopeOperations.setApiUser(by.id());
     AbandonInput in = new AbandonInput();
     if (notify != null) {
       in.notify = notify;
@@ -275,7 +275,7 @@
   private void addReviewerToReviewableChange(Adder adder) throws Exception {
     StagedChange sc = stageReviewableChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
-    addReviewer(adder, sc.changeId, sc.owner, reviewer.email);
+    addReviewer(adder, sc.changeId, sc.owner, reviewer.email());
     // TODO(logan): Should CCs be included?
     assertThat(sender)
         .sent("newchange", sc)
@@ -299,7 +299,7 @@
   private void addReviewerToReviewableChangeByOwnerCcingSelf(Adder adder) throws Exception {
     StagedChange sc = stageReviewableChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
-    addReviewer(adder, sc.changeId, sc.owner, reviewer.email, CC_ON_OWN_COMMENTS, null);
+    addReviewer(adder, sc.changeId, sc.owner, reviewer.email(), CC_ON_OWN_COMMENTS, null);
     // TODO(logan): Should CCs be included?
     assertThat(sender)
         .sent("newchange", sc)
@@ -324,7 +324,7 @@
     TestAccount other = accountCreator.create("other", "other@example.com", "other");
     StagedChange sc = stageReviewableChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
-    addReviewer(adder, sc.changeId, other, reviewer.email);
+    addReviewer(adder, sc.changeId, other, reviewer.email());
     // TODO(logan): Should CCs be included?
     assertThat(sender)
         .sent("newchange", sc)
@@ -349,7 +349,7 @@
     TestAccount other = accountCreator.create("other", "other@example.com", "other");
     StagedChange sc = stageReviewableChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
-    addReviewer(adder, sc.changeId, other, reviewer.email, CC_ON_OWN_COMMENTS, null);
+    addReviewer(adder, sc.changeId, other, reviewer.email(), CC_ON_OWN_COMMENTS, null);
     // TODO(logan): Should CCs be included?
     assertThat(sender)
         .sent("newchange", sc)
@@ -397,7 +397,7 @@
   private void addReviewerToWipChange(Adder adder) throws Exception {
     StagedChange sc = stageWipChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
-    addReviewer(adder, sc.changeId, sc.owner, reviewer.email);
+    addReviewer(adder, sc.changeId, sc.owner, reviewer.email());
     assertThat(sender).didNotSend();
   }
 
@@ -415,7 +415,7 @@
   public void addReviewerToReviewableWipChangeSingly() throws Exception {
     StagedChange sc = stageReviewableWipChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
-    addReviewer(singly(), sc.changeId, sc.owner, reviewer.email);
+    addReviewer(singly(), sc.changeId, sc.owner, reviewer.email());
     // TODO(dborowitz): In theory this should match the batch case, but we don't currently pass
     // enough info into AddReviewersEmail#emailReviewers to distinguish the reviewStarted case.
     // Complicating the emailReviewers arguments is not the answer; this needs to be rewritten.
@@ -427,7 +427,7 @@
   public void addReviewerToReviewableWipChangeBatch() throws Exception {
     StagedChange sc = stageReviewableWipChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
-    addReviewer(batch(), sc.changeId, sc.owner, reviewer.email);
+    addReviewer(batch(), sc.changeId, sc.owner, reviewer.email());
     // For a review-started WIP change, same as in the notify=ALL case. It's not especially
     // important to notify just because a reviewer is added, but we do want to notify in the other
     // case that hits this codepath: posting an actual review.
@@ -442,7 +442,7 @@
   private void addReviewerToWipChangeNotifyAll(Adder adder) throws Exception {
     StagedChange sc = stageWipChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
-    addReviewer(adder, sc.changeId, sc.owner, reviewer.email, NotifyHandling.ALL);
+    addReviewer(adder, sc.changeId, sc.owner, reviewer.email(), NotifyHandling.ALL);
     // TODO(logan): Should CCs be included?
     assertThat(sender)
         .sent("newchange", sc)
@@ -466,7 +466,7 @@
   private void addReviewerToReviewableChangeNotifyOwnerReviewers(Adder adder) throws Exception {
     StagedChange sc = stageReviewableChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
-    addReviewer(adder, sc.changeId, sc.owner, reviewer.email, OWNER_REVIEWERS);
+    addReviewer(adder, sc.changeId, sc.owner, reviewer.email(), OWNER_REVIEWERS);
     // TODO(logan): Should CCs be included?
     assertThat(sender)
         .sent("newchange", sc)
@@ -491,7 +491,7 @@
       throws Exception {
     StagedChange sc = stageReviewableChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
-    addReviewer(adder, sc.changeId, sc.owner, reviewer.email, CC_ON_OWN_COMMENTS, OWNER);
+    addReviewer(adder, sc.changeId, sc.owner, reviewer.email(), CC_ON_OWN_COMMENTS, OWNER);
     assertThat(sender).didNotSend();
   }
 
@@ -509,7 +509,7 @@
       throws Exception {
     StagedChange sc = stageReviewableChange();
     TestAccount reviewer = accountCreator.create("added", "added@example.com", "added");
-    addReviewer(adder, sc.changeId, sc.owner, reviewer.email, CC_ON_OWN_COMMENTS, NONE);
+    addReviewer(adder, sc.changeId, sc.owner, reviewer.email(), CC_ON_OWN_COMMENTS, NONE);
     assertThat(sender).didNotSend();
   }
 
@@ -623,7 +623,7 @@
       @Nullable NotifyHandling notify)
       throws Exception {
     setEmailStrategy(by, emailStrategy);
-    requestScopeOperations.setApiUser(by.getId());
+    requestScopeOperations.setApiUser(by.id());
     adder.addReviewer(changeId, reviewer, notify);
   }
 
@@ -895,7 +895,7 @@
   @Test
   public void addReviewerOnWipChangeAndStartReview() throws Exception {
     StagedChange sc = stageWipChange();
-    ReviewInput in = ReviewInput.noScore().reviewer(other.email).setWorkInProgress(false);
+    ReviewInput in = ReviewInput.noScore().reviewer(other.email()).setWorkInProgress(false);
     gApi.changes().id(sc.changeId).revision("current").review(in);
     assertThat(sender)
         .sent("comment", sc)
@@ -990,9 +990,9 @@
     assertThat(sender).didNotSend();
 
     // Toggle workInProgress flag for owner
-    GeneralPreferencesInfo prefs = gApi.accounts().id(sc.owner.id.get()).getPreferences();
+    GeneralPreferencesInfo prefs = gApi.accounts().id(sc.owner.id().get()).getPreferences();
     prefs.workInProgressByDefault = true;
-    gApi.accounts().id(sc.owner.id.get()).setPreferences(prefs);
+    gApi.accounts().id(sc.owner.id().get()).setPreferences(prefs);
 
     // Create another change without notification that should be wip
     StagedPreChange spc = stagePreChange("refs/for/master");
@@ -1000,10 +1000,10 @@
     assertThat(sender).didNotSend();
 
     // Clean up workInProgressByDefault by owner
-    prefs = gApi.accounts().id(sc.owner.id.get()).getPreferences();
+    prefs = gApi.accounts().id(sc.owner.id().get()).getPreferences();
     Truth.assertThat(prefs.workInProgressByDefault).isTrue();
     prefs.workInProgressByDefault = false;
-    gApi.accounts().id(sc.owner.id.get()).setPreferences(prefs);
+    gApi.accounts().id(sc.owner.id().get()).setPreferences(prefs);
   }
 
   @Test
@@ -1040,7 +1040,8 @@
     StagedPreChange spc =
         stagePreChange(
             "refs/for/master",
-            users -> ImmutableList.of("r=" + users.reviewer.username, "cc=" + users.ccer.username));
+            users ->
+                ImmutableList.of("r=" + users.reviewer.username(), "cc=" + users.ccer.username()));
     FakeEmailSenderSubject subject =
         assertThat(sender).sent("newchange", spc).to(spc.reviewer, spc.watchingProjectOwner);
     subject.cc(spc.ccer);
@@ -1072,7 +1073,7 @@
   @Test
   public void deleteReviewerFromReviewableChange() throws Exception {
     StagedChange sc = stageReviewableChangeWithExtraReviewer();
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     removeReviewer(sc, extraReviewer);
     assertThat(sender)
         .sent("deleteReviewer", sc)
@@ -1104,7 +1105,7 @@
   @Test
   public void deleteReviewerFromReviewableChangeByAdmin() throws Exception {
     StagedChange sc = stageReviewableChangeWithExtraReviewer();
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     removeReviewer(sc, extraReviewer);
     assertThat(sender)
         .sent("deleteReviewer", sc)
@@ -1121,7 +1122,7 @@
   public void deleteReviewerFromReviewableChangeByAdminCcingSelf() throws Exception {
     StagedChange sc = stageReviewableChangeWithExtraReviewer();
     setEmailStrategy(admin, EmailStrategy.CC_ON_OWN_COMMENTS);
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     removeReviewer(sc, extraReviewer);
     assertThat(sender)
         .sent("deleteReviewer", sc)
@@ -1137,7 +1138,7 @@
   @Test
   public void deleteCcerFromReviewableChange() throws Exception {
     StagedChange sc = stageReviewableChangeWithExtraReviewer();
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     removeReviewer(sc, extraCcer);
     assertThat(sender)
         .sent("deleteReviewer", sc)
@@ -1153,7 +1154,7 @@
   @Test
   public void deleteReviewerFromReviewableChangeNotifyOwnerReviewers() throws Exception {
     StagedChange sc = stageReviewableChangeWithExtraReviewer();
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     removeReviewer(sc, extraReviewer, NotifyHandling.OWNER_REVIEWERS);
     assertThat(sender)
         .sent("deleteReviewer", sc)
@@ -1212,7 +1213,7 @@
   @Test
   public void deleteReviewerFromWipChangeNotifyAll() throws Exception {
     StagedChange sc = stageWipChangeWithExtraReviewer();
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     removeReviewer(sc, extraReviewer, NotifyHandling.ALL);
     assertThat(sender)
         .sent("deleteReviewer", sc)
@@ -1229,7 +1230,7 @@
   public void deleteReviewerWithApprovalFromWipChange() throws Exception {
     StagedChange sc = stageWipChangeWithExtraReviewer();
     recommend(sc, extraReviewer);
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     removeReviewer(sc, extraReviewer);
     assertThat(sender).sent("deleteReviewer", sc).to(extraReviewer).noOneElse();
     assertThat(sender).didNotSend();
@@ -1251,7 +1252,7 @@
   }
 
   private void recommend(StagedChange sc, TestAccount by) throws Exception {
-    requestScopeOperations.setApiUser(by.getId());
+    requestScopeOperations.setApiUser(by.id());
     gApi.changes().id(sc.changeId).revision("current").review(ReviewInput.recommend());
   }
 
@@ -1263,9 +1264,9 @@
     StagedChange sc = stager.stage();
     ReviewInput in =
         ReviewInput.noScore()
-            .reviewer(extraReviewer.email)
-            .reviewer(extraCcer.email, ReviewerState.CC, false);
-    requestScopeOperations.setApiUser(extraReviewer.getId());
+            .reviewer(extraReviewer.email())
+            .reviewer(extraCcer.email(), ReviewerState.CC, false);
+    requestScopeOperations.setApiUser(extraReviewer.id());
     gApi.changes().id(sc.changeId).revision("current").review(in);
     sender.clear();
     return sc;
@@ -1289,7 +1290,7 @@
 
   private void removeReviewer(StagedChange sc, TestAccount account) throws Exception {
     sender.clear();
-    gApi.changes().id(sc.changeId).reviewer(account.email).remove();
+    gApi.changes().id(sc.changeId).reviewer(account.email()).remove();
   }
 
   private void removeReviewer(StagedChange sc, TestAccount account, NotifyHandling notify)
@@ -1297,7 +1298,7 @@
     sender.clear();
     DeleteReviewerInput in = new DeleteReviewerInput();
     in.notify = notify;
-    gApi.changes().id(sc.changeId).reviewer(account.email).remove(in);
+    gApi.changes().id(sc.changeId).reviewer(account.email()).remove(in);
   }
 
   /*
@@ -1308,7 +1309,7 @@
   public void deleteVoteFromReviewableChange() throws Exception {
     StagedChange sc = stageReviewableChangeWithExtraReviewer();
     recommend(sc, extraReviewer);
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     deleteVote(sc, extraReviewer);
     assertThat(sender)
         .sent("deleteVote", sc)
@@ -1325,7 +1326,7 @@
     StagedChange sc = stageReviewableChangeWithExtraReviewer();
     recommend(sc, extraReviewer);
     setEmailStrategy(sc.owner, CC_ON_OWN_COMMENTS);
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     deleteVote(sc, extraReviewer);
     assertThat(sender)
         .sent("deleteVote", sc)
@@ -1342,7 +1343,7 @@
   public void deleteVoteFromReviewableChangeByAdmin() throws Exception {
     StagedChange sc = stageReviewableChangeWithExtraReviewer();
     recommend(sc, extraReviewer);
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     deleteVote(sc, extraReviewer);
     assertThat(sender)
         .sent("deleteVote", sc)
@@ -1360,7 +1361,7 @@
     StagedChange sc = stageReviewableChangeWithExtraReviewer();
     recommend(sc, extraReviewer);
     setEmailStrategy(admin, EmailStrategy.CC_ON_OWN_COMMENTS);
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     deleteVote(sc, extraReviewer);
     assertThat(sender)
         .sent("deleteVote", sc)
@@ -1377,7 +1378,7 @@
   public void deleteVoteFromReviewableChangeNotifyOwnerReviewers() throws Exception {
     StagedChange sc = stageReviewableChangeWithExtraReviewer();
     recommend(sc, extraReviewer);
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     deleteVote(sc, extraReviewer, NotifyHandling.OWNER_REVIEWERS);
     assertThat(sender)
         .sent("deleteVote", sc)
@@ -1392,7 +1393,7 @@
     StagedChange sc = stageReviewableChangeWithExtraReviewer();
     recommend(sc, extraReviewer);
     setEmailStrategy(sc.owner, CC_ON_OWN_COMMENTS);
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     deleteVote(sc, extraReviewer, NotifyHandling.OWNER_REVIEWERS);
     assertThat(sender)
         .sent("deleteVote", sc)
@@ -1407,7 +1408,7 @@
   public void deleteVoteFromReviewableChangeNotifyOwner() throws Exception {
     StagedChange sc = stageReviewableChangeWithExtraReviewer();
     recommend(sc, extraReviewer);
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     deleteVote(sc, extraReviewer, NotifyHandling.OWNER);
     assertThat(sender).sent("deleteVote", sc).to(sc.owner).noOneElse();
     assertThat(sender).didNotSend();
@@ -1417,7 +1418,7 @@
   public void deleteVoteFromReviewableChangeNotifyNone() throws Exception {
     StagedChange sc = stageReviewableChangeWithExtraReviewer();
     recommend(sc, extraReviewer);
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     deleteVote(sc, extraReviewer, NotifyHandling.NONE);
     assertThat(sender).didNotSend();
   }
@@ -1427,7 +1428,7 @@
     StagedChange sc = stageReviewableChangeWithExtraReviewer();
     recommend(sc, extraReviewer);
     setEmailStrategy(sc.owner, CC_ON_OWN_COMMENTS);
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     deleteVote(sc, extraReviewer, NotifyHandling.NONE);
     assertThat(sender).didNotSend();
   }
@@ -1436,7 +1437,7 @@
   public void deleteVoteFromReviewableWipChange() throws Exception {
     StagedChange sc = stageReviewableWipChangeWithExtraReviewer();
     recommend(sc, extraReviewer);
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     deleteVote(sc, extraReviewer);
     assertThat(sender)
         .sent("deleteVote", sc)
@@ -1452,7 +1453,7 @@
   public void deleteVoteFromWipChange() throws Exception {
     StagedChange sc = stageWipChangeWithExtraReviewer();
     recommend(sc, extraReviewer);
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     deleteVote(sc, extraReviewer);
     assertThat(sender)
         .sent("deleteVote", sc)
@@ -1466,7 +1467,7 @@
 
   private void deleteVote(StagedChange sc, TestAccount account) throws Exception {
     sender.clear();
-    gApi.changes().id(sc.changeId).reviewer(account.email).deleteVote("Code-Review");
+    gApi.changes().id(sc.changeId).reviewer(account.email()).deleteVote("Code-Review");
   }
 
   private void deleteVote(StagedChange sc, TestAccount account, NotifyHandling notify)
@@ -1475,7 +1476,7 @@
     DeleteVoteInput in = new DeleteVoteInput();
     in.label = "Code-Review";
     in.notify = notify;
-    gApi.changes().id(sc.changeId).reviewer(account.email).deleteVote(in);
+    gApi.changes().id(sc.changeId).reviewer(account.email()).deleteVote(in);
   }
 
   /*
@@ -1623,7 +1624,7 @@
   private void merge(String changeId, TestAccount by, EmailStrategy emailStrategy)
       throws Exception {
     setEmailStrategy(by, emailStrategy);
-    requestScopeOperations.setApiUser(by.getId());
+    requestScopeOperations.setApiUser(by.id());
     gApi.changes().id(changeId).revision("current").submit();
   }
 
@@ -1635,7 +1636,7 @@
       String changeId, TestAccount by, EmailStrategy emailStrategy, NotifyHandling notify)
       throws Exception {
     setEmailStrategy(by, emailStrategy);
-    requestScopeOperations.setApiUser(by.getId());
+    requestScopeOperations.setApiUser(by.id());
     SubmitInput in = new SubmitInput();
     in.notify = notify;
     gApi.changes().id(changeId).revision("current").submit(in);
@@ -1643,7 +1644,7 @@
 
   private StagedChange stageChangeReadyForMerge() throws Exception {
     StagedChange sc = stageReviewableChange();
-    requestScopeOperations.setApiUser(sc.reviewer.getId());
+    requestScopeOperations.setApiUser(sc.reviewer.id());
     gApi.changes().id(sc.changeId).revision("current").review(ReviewInput.approve());
     sender.clear();
     return sc;
@@ -1818,7 +1819,7 @@
   public void newPatchSetOnReviewableChangeAddingReviewer() throws Exception {
     StagedChange sc = stageReviewableChange();
     TestAccount newReviewer = sc.testAccount("newReviewer");
-    pushTo(sc, "refs/for/master%r=" + newReviewer.username, sc.owner);
+    pushTo(sc, "refs/for/master%r=" + newReviewer.username(), sc.owner);
     assertThat(sender)
         .sent("newpatchset", sc)
         .to(sc.reviewer, newReviewer)
@@ -1834,7 +1835,7 @@
   public void newPatchSetOnWipChangeAddingReviewer() throws Exception {
     StagedChange sc = stageWipChange();
     TestAccount newReviewer = sc.testAccount("newReviewer");
-    pushTo(sc, "refs/for/master%r=" + newReviewer.username, sc.owner);
+    pushTo(sc, "refs/for/master%r=" + newReviewer.username(), sc.owner);
     assertThat(sender).didNotSend();
   }
 
@@ -1842,7 +1843,7 @@
   public void newPatchSetOnWipChangeAddingReviewerNotifyAll() throws Exception {
     StagedChange sc = stageWipChange();
     TestAccount newReviewer = sc.testAccount("newReviewer");
-    pushTo(sc, "refs/for/master%notify=ALL,r=" + newReviewer.username, sc.owner);
+    pushTo(sc, "refs/for/master%notify=ALL,r=" + newReviewer.username(), sc.owner);
     assertThat(sender)
         .sent("newpatchset", sc)
         .to(sc.reviewer, newReviewer)
@@ -1876,7 +1877,7 @@
   private void pushTo(StagedChange sc, String ref, TestAccount by, EmailStrategy emailStrategy)
       throws Exception {
     setEmailStrategy(by, emailStrategy);
-    pushFactory.create(by.getIdent(), sc.repo, sc.changeId).to(ref).assertOkStatus();
+    pushFactory.create(by.newIdent(), sc.repo, sc.changeId).to(ref).assertOkStatus();
   }
 
   @Test
@@ -2142,7 +2143,7 @@
   private void restore(String changeId, TestAccount by, EmailStrategy emailStrategy)
       throws Exception {
     setEmailStrategy(by, emailStrategy);
-    requestScopeOperations.setApiUser(by.getId());
+    requestScopeOperations.setApiUser(by.id());
     gApi.changes().id(changeId).restore();
   }
 
@@ -2251,7 +2252,7 @@
 
   private StagedChange stageChange() throws Exception {
     StagedChange sc = stageReviewableChange();
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.changes().id(sc.changeId).revision("current").review(ReviewInput.approve());
     gApi.changes().id(sc.changeId).revision("current").submit();
     sender.clear();
@@ -2265,7 +2266,7 @@
   private void revert(StagedChange sc, TestAccount by, EmailStrategy emailStrategy)
       throws Exception {
     setEmailStrategy(by, emailStrategy);
-    requestScopeOperations.setApiUser(by.getId());
+    requestScopeOperations.setApiUser(by.id());
     gApi.changes().id(sc.changeId).revert();
   }
 
@@ -2393,9 +2394,9 @@
   private void assign(StagedChange sc, TestAccount by, TestAccount to, EmailStrategy emailStrategy)
       throws Exception {
     setEmailStrategy(by, emailStrategy);
-    requestScopeOperations.setApiUser(by.getId());
+    requestScopeOperations.setApiUser(by.id());
     AssigneeInput in = new AssigneeInput();
-    in.assignee = to.email;
+    in.assignee = to.email();
     gApi.changes().id(sc.changeId).setAssignee(in);
   }
 
@@ -2441,7 +2442,7 @@
   }
 
   private void startReview(StagedChange sc) throws Exception {
-    requestScopeOperations.setApiUser(sc.owner.getId());
+    requestScopeOperations.setApiUser(sc.owner.id());
     gApi.changes().id(sc.changeId).setReadyForReview();
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
index 8ad9f96..1386aec 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailMetadataIT.java
@@ -62,7 +62,7 @@
   @Test
   public void metadataOnNewChange() throws Exception {
     PushOneCommit.Result newChange = createChange();
-    gApi.changes().id(newChange.getChangeId()).addReviewer(user.getId().toString());
+    gApi.changes().id(newChange.getChangeId()).addReviewer(user.id().toString());
 
     List<FakeEmailSender.Message> emails = sender.getMessages();
     assertThat(emails).hasSize(1);
@@ -89,14 +89,14 @@
   @Test
   public void metadataOnNewComment() throws Exception {
     PushOneCommit.Result newChange = createChange();
-    gApi.changes().id(newChange.getChangeId()).addReviewer(user.getId().toString());
+    gApi.changes().id(newChange.getChangeId()).addReviewer(user.id().toString());
     sender.clear();
 
     // Review change
     ReviewInput input = new ReviewInput();
     input.message = "Test";
     revision(newChange).review(input);
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     Collection<ChangeMessageInfo> result =
         gApi.changes().id(newChange.getChangeId()).get().messages;
     assertThat(result).isNotEmpty();
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
index b8380f5..f917fd8 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailProcessorIT.java
@@ -165,7 +165,7 @@
     b.textContent(txt + textFooterForChange(changeInfo._number, ts));
 
     // Set account state to inactive
-    accountOperations.account(user.id).forUpdate().inactive().update();
+    accountOperations.account(user.id()).forUpdate().inactive().update();
 
     mailProcessor.process(b.build());
     comments = gApi.changes().id(changeId).current().commentsAsList();
@@ -189,7 +189,7 @@
         newPlaintextBody(getChangeUrl(changeInfo) + "/1", "Test Message", null, null, null);
     MailMessage.Builder b =
         messageBuilderWithDefaultFields()
-            .from(user.emailAddress)
+            .from(user.getEmailAddress())
             .textContent(txt + textFooterForChange(changeInfo._number, ts));
 
     sender.clear();
@@ -211,7 +211,7 @@
         newPlaintextBody(getChangeUrl(changeInfo) + "/1", "Test Message", null, null, null);
     MailMessage.Builder b =
         messageBuilderWithDefaultFields()
-            .from(user.emailAddress)
+            .from(user.getEmailAddress())
             .textContent(txt + textFooterForChange(changeInfo._number, ts));
 
     sender.clear();
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java b/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
index 8d21b5b..c395c81 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
@@ -41,7 +41,7 @@
     // Check that the user's email was added as Reply-To
     assertThat(sender.getMessages()).hasSize(1);
     Map<String, EmailHeader> headers = sender.getMessages().iterator().next().headers();
-    assertThat(headerString(headers, "Reply-To")).contains(user.email);
+    assertThat(headerString(headers, "Reply-To")).contains(user.email());
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/acceptance/server/mail/NotificationMailFormatIT.java b/javatests/com/google/gerrit/acceptance/server/mail/NotificationMailFormatIT.java
index 43a3642..628b90c 100644
--- a/javatests/com/google/gerrit/acceptance/server/mail/NotificationMailFormatIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/mail/NotificationMailFormatIT.java
@@ -34,11 +34,11 @@
     // Set user preference to receive only plaintext content
     GeneralPreferencesInfo i = new GeneralPreferencesInfo();
     i.emailFormat = EmailFormat.PLAINTEXT;
-    gApi.accounts().id(admin.getId().toString()).setPreferences(i);
+    gApi.accounts().id(admin.id().toString()).setPreferences(i);
 
     // Create change as admin and review as user
     PushOneCommit.Result r = createChange();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(r.getChangeId()).current().review(ReviewInput.recommend());
 
     // Check that admin has received only plaintext content
@@ -46,20 +46,20 @@
     FakeEmailSender.Message m = sender.getMessages().get(0);
     assertThat(m.body()).isNotNull();
     assertThat(m.htmlBody()).isNull();
-    assertMailReplyTo(m, admin.email);
-    assertMailReplyTo(m, user.email);
+    assertMailReplyTo(m, admin.email());
+    assertMailReplyTo(m, user.email());
 
     // Reset user preference
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     i.emailFormat = EmailFormat.HTML_PLAINTEXT;
-    gApi.accounts().id(admin.getId().toString()).setPreferences(i);
+    gApi.accounts().id(admin.id().toString()).setPreferences(i);
   }
 
   @Test
   public void userReceivesHtmlAndPlaintextEmail() throws Exception {
     // Create change as admin and review as user
     PushOneCommit.Result r = createChange();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.changes().id(r.getChangeId()).current().review(ReviewInput.recommend());
 
     // Check that admin has received both HTML and plaintext content
@@ -67,7 +67,7 @@
     FakeEmailSender.Message m = sender.getMessages().get(0);
     assertThat(m.body()).isNotNull();
     assertThat(m.htmlBody()).isNotNull();
-    assertMailReplyTo(m, admin.email);
-    assertMailReplyTo(m, user.email);
+    assertMailReplyTo(m, admin.email());
+    assertMailReplyTo(m, user.email());
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
index 1ea64ca..748c4ea 100644
--- a/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/notedb/NoteDbOnlyIT.java
@@ -276,7 +276,7 @@
   }
 
   private BatchUpdate newBatchUpdate(BatchUpdate.Factory buf) {
-    return buf.create(project, identifiedUserFactory.create(user.getId()), TimeUtil.nowTs());
+    return buf.create(project, identifiedUserFactory.create(user.id()), TimeUtil.nowTs());
   }
 
   private Optional<ObjectId> getRef(String name) throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendConditionIT.java b/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendConditionIT.java
index a72cd33..2919e5f 100644
--- a/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendConditionIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/permissions/PermissionBackendConditionIT.java
@@ -163,10 +163,10 @@
   }
 
   private CurrentUser user() {
-    return identifiedUserFactory.create(user.id);
+    return identifiedUserFactory.create(user.id());
   }
 
   private CurrentUser admin() {
-    return identifiedUserFactory.create(admin.id);
+    return identifiedUserFactory.create(admin.id());
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java b/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
index 53e21b6..6cbe40e 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
@@ -178,7 +178,7 @@
     saveLabelConfig();
     PushOneCommit.Result r = createChange();
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     gApi.changes().id(r.getChangeId()).addReviewer(in);
 
     ReviewInput input = new ReviewInput().label(P.getName(), 0);
diff --git a/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java b/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
index 03f580a..4ed16ee 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/ProjectWatchIT.java
@@ -63,19 +63,19 @@
 
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), testRepo, "original subject", "a", "a1")
+            .create(admin.newIdent(), testRepo, "original subject", "a", "a1")
             .to("refs/for/master");
     r.assertOkStatus();
 
     r =
         pushFactory
-            .create(admin.getIdent(), testRepo, "super sekret subject", "a", "a2", r.getChangeId())
+            .create(admin.newIdent(), testRepo, "super sekret subject", "a", "a2", r.getChangeId())
             .to("refs/for/master");
     r.assertOkStatus();
 
     r =
         pushFactory
-            .create(admin.getIdent(), testRepo, "back to original subject", "a", "a3")
+            .create(admin.newIdent(), testRepo, "back to original subject", "a", "a3")
             .to("refs/for/master");
     r.assertOkStatus();
 
@@ -104,13 +104,13 @@
     sender.clear();
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), testRepo, "private change", "a", "a1")
+            .create(admin.newIdent(), testRepo, "private change", "a", "a1")
             .to("refs/for/master%private");
     r.assertOkStatus();
 
     assertThat(sender.getMessages()).isEmpty();
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     ReviewInput in = new ReviewInput();
     in.message = "comment";
     gApi.changes().id(r.getChangeId()).current().review(in);
@@ -134,14 +134,14 @@
     }
 
     PushOneCommit.Result r =
-        pushFactory.create(admin.getIdent(), testRepo, "subject", "a", "a1").to("refs/for/master");
+        pushFactory.create(admin.newIdent(), testRepo, "subject", "a", "a1").to("refs/for/master");
     r.assertOkStatus();
 
     sender.clear();
 
     r =
         pushFactory
-            .create(admin.getIdent(), testRepo, "subject", "a", "a2", r.getChangeId())
+            .create(admin.newIdent(), testRepo, "subject", "a", "a2", r.getChangeId())
             .to("refs/for/master%private");
     r.assertOkStatus();
 
@@ -165,13 +165,13 @@
     sender.clear();
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), testRepo, "wip change", "a", "a1")
+            .create(admin.newIdent(), testRepo, "wip change", "a", "a1")
             .to("refs/for/master%wip");
     r.assertOkStatus();
 
     assertThat(sender.getMessages()).isEmpty();
 
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     ReviewInput in = new ReviewInput();
     in.message = "comment";
     gApi.changes().id(r.getChangeId()).current().review(in);
@@ -194,14 +194,14 @@
     }
 
     PushOneCommit.Result r =
-        pushFactory.create(admin.getIdent(), testRepo, "subject", "a", "a1").to("refs/for/master");
+        pushFactory.create(admin.newIdent(), testRepo, "subject", "a", "a1").to("refs/for/master");
     r.assertOkStatus();
 
     sender.clear();
 
     r =
         pushFactory
-            .create(admin.getIdent(), testRepo, "subject", "a", "a2", r.getChangeId())
+            .create(admin.newIdent(), testRepo, "subject", "a", "a2", r.getChangeId())
             .to("refs/for/master%wip");
     r.assertOkStatus();
 
@@ -212,16 +212,16 @@
   public void watchProject() throws Exception {
     // watch project
     String watchedProject = projectOperations.newProject().create().get();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     watch(watchedProject);
 
     // push a change to watched project -> should trigger email notification
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     TestRepository<InMemoryRepository> watchedRepo =
         cloneProject(new Project.NameKey(watchedProject), admin);
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), watchedRepo, "TRIGGER", "a", "a1")
+            .create(admin.newIdent(), watchedRepo, "TRIGGER", "a", "a1")
             .to("refs/for/master");
     r.assertOkStatus();
 
@@ -232,7 +232,7 @@
         cloneProject(new Project.NameKey(notWatchedProject), admin);
     r =
         pushFactory
-            .create(admin.getIdent(), notWatchedRepo, "DONT_TRIGGER", "a", "a1")
+            .create(admin.newIdent(), notWatchedRepo, "DONT_TRIGGER", "a", "a1")
             .to("refs/for/master");
     r.assertOkStatus();
 
@@ -240,7 +240,7 @@
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress);
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
     assertThat(m.body()).contains("Change subject: TRIGGER\n");
     assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
   }
@@ -249,7 +249,7 @@
   public void watchFile() throws Exception {
     String watchedProject = projectOperations.newProject().create().get();
     String otherWatchedProject = projectOperations.newProject().create().get();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     // watch file in project as user
     watch(watchedProject, "file:a.txt");
@@ -259,12 +259,12 @@
 
     // push a change to watched file -> should trigger email notification for
     // user
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     TestRepository<InMemoryRepository> watchedRepo =
         cloneProject(new Project.NameKey(watchedProject), admin);
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), watchedRepo, "TRIGGER", "a.txt", "a1")
+            .create(admin.newIdent(), watchedRepo, "TRIGGER", "a.txt", "a1")
             .to("refs/for/master");
     r.assertOkStatus();
 
@@ -272,21 +272,21 @@
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress);
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
     assertThat(m.body()).contains("Change subject: TRIGGER\n");
     assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
     sender.clear();
 
     // watch project as user2
     TestAccount user2 = accountCreator.create("user2", "user2@test.com", "User2");
-    requestScopeOperations.setApiUser(user2.getId());
+    requestScopeOperations.setApiUser(user2.id());
     watch(watchedProject);
 
     // push a change to non-watched file -> should not trigger email
     // notification for user, only for user2
     r =
         pushFactory
-            .create(admin.getIdent(), watchedRepo, "TRIGGER_USER2", "b.txt", "b1")
+            .create(admin.newIdent(), watchedRepo, "TRIGGER_USER2", "b.txt", "b1")
             .to("refs/for/master");
     r.assertOkStatus();
 
@@ -294,7 +294,7 @@
     messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     m = messages.get(0);
-    assertThat(m.rcpt()).containsExactly(user2.emailAddress);
+    assertThat(m.rcpt()).containsExactly(user2.getEmailAddress());
     assertThat(m.body()).contains("Change subject: TRIGGER_USER2\n");
     assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
   }
@@ -302,18 +302,18 @@
   @Test
   public void watchKeyword() throws Exception {
     String watchedProject = projectOperations.newProject().create().get();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     // watch keyword in project as user
     watch(watchedProject, "multimaster");
 
     // push a change with keyword -> should trigger email notification
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     TestRepository<InMemoryRepository> watchedRepo =
         cloneProject(new Project.NameKey(watchedProject), admin);
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), watchedRepo, "Document multimaster setup", "a.txt", "a1")
+            .create(admin.newIdent(), watchedRepo, "Document multimaster setup", "a.txt", "a1")
             .to("refs/for/master");
     r.assertOkStatus();
 
@@ -321,7 +321,7 @@
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress);
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
     assertThat(m.body()).contains("Change subject: Document multimaster setup\n");
     assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
     sender.clear();
@@ -329,7 +329,7 @@
     // push a change without keyword -> should not trigger email notification
     r =
         pushFactory
-            .create(admin.getIdent(), watchedRepo, "Cleanup cache implementation", "b.txt", "b1")
+            .create(admin.newIdent(), watchedRepo, "Cleanup cache implementation", "b.txt", "b1")
             .to("refs/for/master");
     r.assertOkStatus();
 
@@ -340,24 +340,24 @@
   @Test
   public void watchAllProjects() throws Exception {
     String anyProject = projectOperations.newProject().create().get();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     // watch the All-Projects project to watch all projects
     watch(allProjects.get());
 
     // push a change to any project -> should trigger email notification
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     TestRepository<InMemoryRepository> anyRepo =
         cloneProject(new Project.NameKey(anyProject), admin);
     PushOneCommit.Result r =
-        pushFactory.create(admin.getIdent(), anyRepo, "TRIGGER", "a", "a1").to("refs/for/master");
+        pushFactory.create(admin.newIdent(), anyRepo, "TRIGGER", "a", "a1").to("refs/for/master");
     r.assertOkStatus();
 
     // assert email notification
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress);
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
     assertThat(m.body()).contains("Change subject: TRIGGER\n");
     assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
   }
@@ -365,7 +365,7 @@
   @Test
   public void watchFileAllProjects() throws Exception {
     String anyProject = projectOperations.newProject().create().get();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     // watch file in All-Projects project as user to watch the file in all
     // projects
@@ -373,12 +373,12 @@
 
     // push a change to watched file in any project -> should trigger email
     // notification for user
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     TestRepository<InMemoryRepository> anyRepo =
         cloneProject(new Project.NameKey(anyProject), admin);
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), anyRepo, "TRIGGER", "a.txt", "a1")
+            .create(admin.newIdent(), anyRepo, "TRIGGER", "a.txt", "a1")
             .to("refs/for/master");
     r.assertOkStatus();
 
@@ -386,21 +386,21 @@
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress);
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
     assertThat(m.body()).contains("Change subject: TRIGGER\n");
     assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
     sender.clear();
 
     // watch project as user2
     TestAccount user2 = accountCreator.create("user2", "user2@test.com", "User2");
-    requestScopeOperations.setApiUser(user2.getId());
+    requestScopeOperations.setApiUser(user2.id());
     watch(anyProject);
 
     // push a change to non-watched file in any project -> should not trigger
     // email notification for user, only for user2
     r =
         pushFactory
-            .create(admin.getIdent(), anyRepo, "TRIGGER_USER2", "b.txt", "b1")
+            .create(admin.newIdent(), anyRepo, "TRIGGER_USER2", "b.txt", "b1")
             .to("refs/for/master");
     r.assertOkStatus();
 
@@ -408,7 +408,7 @@
     messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     m = messages.get(0);
-    assertThat(m.rcpt()).containsExactly(user2.emailAddress);
+    assertThat(m.rcpt()).containsExactly(user2.getEmailAddress());
     assertThat(m.body()).contains("Change subject: TRIGGER_USER2\n");
     assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
   }
@@ -416,19 +416,19 @@
   @Test
   public void watchKeywordAllProjects() throws Exception {
     String anyProject = projectOperations.newProject().create().get();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
 
     // watch keyword in project as user
     watch(allProjects.get(), "multimaster");
 
     // push a change with keyword to any project -> should trigger email
     // notification
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     TestRepository<InMemoryRepository> anyRepo =
         cloneProject(new Project.NameKey(anyProject), admin);
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), anyRepo, "Document multimaster setup", "a.txt", "a1")
+            .create(admin.newIdent(), anyRepo, "Document multimaster setup", "a.txt", "a1")
             .to("refs/for/master");
     r.assertOkStatus();
 
@@ -436,7 +436,7 @@
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
-    assertThat(m.rcpt()).containsExactly(user.emailAddress);
+    assertThat(m.rcpt()).containsExactly(user.getEmailAddress());
     assertThat(m.body()).contains("Change subject: Document multimaster setup\n");
     assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
     sender.clear();
@@ -445,7 +445,7 @@
     // notification
     r =
         pushFactory
-            .create(admin.getIdent(), anyRepo, "Cleanup cache implementation", "b.txt", "b1")
+            .create(admin.newIdent(), anyRepo, "Cleanup cache implementation", "b.txt", "b1")
             .to("refs/for/master");
     r.assertOkStatus();
 
@@ -457,27 +457,27 @@
   public void watchProjectNoNotificationForIgnoredChange() throws Exception {
     // watch project
     String watchedProject = projectOperations.newProject().create().get();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     watch(watchedProject);
 
     // push a change to watched project
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     TestRepository<InMemoryRepository> watchedRepo =
         cloneProject(new Project.NameKey(watchedProject), admin);
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), watchedRepo, "ignored change", "a", "a1")
+            .create(admin.newIdent(), watchedRepo, "ignored change", "a", "a1")
             .to("refs/for/master");
     r.assertOkStatus();
 
     // ignore the change
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.accounts().self().setStars(r.getChangeId(), new StarsInput(ImmutableSet.of(IGNORE_LABEL)));
 
     sender.clear();
 
     // post a comment -> should not trigger email notification since user ignored the change
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     ReviewInput in = new ReviewInput();
     in.message = "comment";
     gApi.changes().id(r.getChangeId()).current().review(in);
@@ -490,16 +490,16 @@
   public void watchProjectNoNotificationForPrivateChange() throws Exception {
     // watch project
     String watchedProject = projectOperations.newProject().create().get();
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     watch(watchedProject);
 
     // push a private change to watched project -> should not trigger email notification
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     TestRepository<InMemoryRepository> watchedRepo =
         cloneProject(new Project.NameKey(watchedProject), admin);
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), watchedRepo, "private change", "a", "a1")
+            .create(admin.newIdent(), watchedRepo, "private change", "a", "a1")
             .to("refs/for/master%private");
     r.assertOkStatus();
 
@@ -522,24 +522,24 @@
         new AccountGroup.UUID(groupThatCanViewPrivateChanges.id));
 
     // watch project as user that can't view private changes
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     watch(watchedProject);
 
     // watch project as user that can view all private change
     TestAccount userThatCanViewPrivateChanges =
         accountCreator.create(
             "user2", "user2@test.com", "User2", groupThatCanViewPrivateChanges.name);
-    requestScopeOperations.setApiUser(userThatCanViewPrivateChanges.getId());
+    requestScopeOperations.setApiUser(userThatCanViewPrivateChanges.id());
     watch(watchedProject);
 
     // push a private change to watched project -> should trigger email notification for
     // userThatCanViewPrivateChanges, but not for user
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     TestRepository<InMemoryRepository> watchedRepo =
         cloneProject(new Project.NameKey(watchedProject), admin);
     PushOneCommit.Result r =
         pushFactory
-            .create(admin.getIdent(), watchedRepo, "TRIGGER", "a", "a1")
+            .create(admin.newIdent(), watchedRepo, "TRIGGER", "a", "a1")
             .to("refs/for/master%private");
     r.assertOkStatus();
 
@@ -547,7 +547,7 @@
     List<Message> messages = sender.getMessages();
     assertThat(messages).hasSize(1);
     Message m = messages.get(0);
-    assertThat(m.rcpt()).containsExactly(userThatCanViewPrivateChanges.emailAddress);
+    assertThat(m.rcpt()).containsExactly(userThatCanViewPrivateChanges.getEmailAddress());
     assertThat(m.body()).contains("Change subject: TRIGGER\n");
     assertThat(m.body()).contains("Gerrit-PatchSet: 1\n");
   }
diff --git a/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java b/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java
index c61b7ad..da3a257 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/ReflogIT.java
@@ -84,7 +84,7 @@
 
   @Test
   public void regularUserIsNotAllowedToGetReflog() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     exception.expect(AuthException.class);
     gApi.projects().name(project.get()).branch("master").reflog();
   }
@@ -100,13 +100,13 @@
       u.save();
     }
 
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     gApi.projects().name(project.get()).branch("master").reflog();
   }
 
   @Test
   public void adminUserIsAllowedToGetReflog() throws Exception {
-    requestScopeOperations.setApiUser(admin.getId());
+    requestScopeOperations.setApiUser(admin.id());
     gApi.projects().name(project.get()).branch("master").reflog();
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java b/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java
index dea83ca..286e5ae 100644
--- a/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/quota/DefaultQuotaBackendIT.java
@@ -57,7 +57,7 @@
 
   @Before
   public void setUp() {
-    identifiedAdmin = identifiedUserFactory.create(admin.id);
+    identifiedAdmin = identifiedUserFactory.create(admin.id());
     resetToStrict(quotaEnforcer);
   }
 
@@ -73,10 +73,10 @@
   @Test
   public void requestTokenForUserAndAccount() {
     QuotaRequestContext ctx =
-        QuotaRequestContext.builder().user(identifiedAdmin).account(user.id).build();
+        QuotaRequestContext.builder().user(identifiedAdmin).account(user.id()).build();
     expect(quotaEnforcer.requestTokens("testGroup", ctx, 1)).andReturn(QuotaResponse.ok());
     replay(quotaEnforcer);
-    assertThat(quotaBackend.user(identifiedAdmin).account(user.id).requestToken("testGroup"))
+    assertThat(quotaBackend.user(identifiedAdmin).account(user.id()).requestToken("testGroup"))
         .isEqualTo(singletonAggregation(QuotaResponse.ok()));
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java b/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java
index 31a8808..8b9ffc5 100644
--- a/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/quota/MultipleQuotaPluginsIT.java
@@ -63,7 +63,7 @@
 
   @Before
   public void setUp() {
-    identifiedAdmin = identifiedUserFactory.create(admin.id);
+    identifiedAdmin = identifiedUserFactory.create(admin.id());
     resetToStrict(quotaEnforcerA);
     resetToStrict(quotaEnforcerB);
   }
diff --git a/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java b/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java
index a555ba4..a075690 100644
--- a/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/quota/RestApiQuotaIT.java
@@ -107,7 +107,7 @@
     expect(quotaBackendWithResource.requestToken("/restapi/accounts/detail:GET"))
         .andReturn(singletonAggregation(QuotaResponse.ok()));
     replay(quotaBackendWithResource);
-    expect(quotaBackendWithUser.account(admin.id)).andReturn(quotaBackendWithResource);
+    expect(quotaBackendWithUser.account(admin.id())).andReturn(quotaBackendWithResource);
     replay(quotaBackendWithUser);
     adminRestSession.get("/accounts/self/detail").assertOK();
     verify(quotaBackendWithUser);
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java b/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
index df8c5af..83782c9 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/IgnoreSelfApprovalRuleIT.java
@@ -63,7 +63,7 @@
 
     // Create change as user
     TestRepository<InMemoryRepository> userTestRepo = cloneProject(project, user);
-    PushOneCommit push = pushFactory.create(user.getIdent(), userTestRepo);
+    PushOneCommit push = pushFactory.create(user.newIdent(), userTestRepo);
     PushOneCommit.Result r = push.to("refs/for/master");
 
     // Approve as admin
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java b/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java
index 8fc32b4..fa13be4 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/PrologRuleEvaluatorIT.java
@@ -87,17 +87,17 @@
     SubmitRecord.Label submitRecordLabel1 = new SubmitRecord.Label();
     submitRecordLabel1.label = "Verified";
     submitRecordLabel1.status = SubmitRecord.Label.Status.REJECT;
-    submitRecordLabel1.appliedBy = admin.id;
+    submitRecordLabel1.appliedBy = admin.id();
 
     SubmitRecord.Label submitRecordLabel2 = new SubmitRecord.Label();
     submitRecordLabel2.label = "Code-Review";
     submitRecordLabel2.status = SubmitRecord.Label.Status.OK;
-    submitRecordLabel2.appliedBy = admin.id;
+    submitRecordLabel2.appliedBy = admin.id();
 
     SubmitRecord.Label submitRecordLabel3 = new SubmitRecord.Label();
     submitRecordLabel3.label = "Any-Label-Name";
     submitRecordLabel3.status = SubmitRecord.Label.Status.REJECT;
-    submitRecordLabel3.appliedBy = user.id;
+    submitRecordLabel3.appliedBy = user.id();
 
     List<Term> terms = new ArrayList<>();
 
@@ -140,7 +140,7 @@
   }
 
   private static StructureTerm makeLabel(String name, String status, TestAccount account) {
-    StructureTerm user = new StructureTerm("user", new IntegerTerm(account.id.get()));
+    StructureTerm user = new StructureTerm("user", new IntegerTerm(account.id().get()));
     return new StructureTerm("label", new StructureTerm(name), new StructureTerm(status, user));
   }
 
@@ -150,7 +150,7 @@
 
   private ChangeData makeChangeData() {
     ChangeData cd = ChangeData.createForTest(project, new Change.Id(1), 1);
-    cd.setChange(TestChanges.newChange(project, admin.id));
+    cd.setChange(TestChanges.newChange(project, admin.id()));
     return cd;
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java b/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
index 53ac70b..c69712c 100644
--- a/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/rules/RulesIT.java
@@ -74,7 +74,7 @@
     modifySubmitRules(
         String.format(
             "gerrit:commit_author(user(%d), '%s', '%s')",
-            user.getId().get(), user.fullName, user.email));
+            user.id().get(), user.fullName(), user.email()));
     assertThat(statusForRule()).isEqualTo(SubmitRecord.Status.OK);
   }
 
@@ -87,7 +87,7 @@
   private SubmitRecord.Status statusForRule() throws Exception {
     String oldHead = getRemoteHead().name();
     PushOneCommit.Result result1 =
-        pushFactory.create(user.getIdent(), testRepo).to("refs/for/master");
+        pushFactory.create(user.newIdent(), testRepo).to("refs/for/master");
     testRepo.reset(oldHead);
     ChangeData cd = result1.getChange();
 
@@ -112,8 +112,8 @@
       testRepo
           .branch(RefNames.REFS_CONFIG)
           .commit()
-          .author(admin.getIdent())
-          .committer(admin.getIdent())
+          .author(admin.newIdent())
+          .committer(admin.newIdent())
           .add("rules.pl", newContent)
           .message("Modify rules.pl")
           .create();
diff --git a/javatests/com/google/gerrit/acceptance/ssh/QueryIT.java b/javatests/com/google/gerrit/acceptance/ssh/QueryIT.java
index 50b2a78d..0f47a4a 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/QueryIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/QueryIT.java
@@ -84,7 +84,7 @@
   public void allReviewersOptionJSON() throws Exception {
     String changeId = createChange().getChangeId();
     AddReviewerInput in = new AddReviewerInput();
-    in.reviewer = user.email;
+    in.reviewer = user.email();
     gApi.changes().id(changeId).addReviewer(in);
 
     List<ChangeAttribute> changes = executeSuccessfulQuery(changeId);
diff --git a/javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java b/javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java
index 7b0088e..bb5b7c9 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/SetReviewersIT.java
@@ -50,13 +50,13 @@
 
   private void setReviewer(boolean add, String id) throws Exception {
     adminSshSession.exec(
-        String.format("gerrit set-reviewers -%s %s %s", add ? "a" : "r", user.email, id));
+        String.format("gerrit set-reviewers -%s %s %s", add ? "a" : "r", user.email(), id));
     adminSshSession.assertSuccess();
     ImmutableSet<Account.Id> reviewers = change.getChange().getReviewers().all();
     if (add) {
-      assertThat(reviewers).contains(user.id);
+      assertThat(reviewers).contains(user.id());
     } else {
-      assertThat(reviewers).doesNotContain(user.id);
+      assertThat(reviewers).doesNotContain(user.id());
     }
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java
index 64dcfc2..e0d6593 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/group/GroupOperationsImplTest.java
@@ -19,6 +19,7 @@
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.truth.Correspondence;
+import com.google.common.truth.Correspondence.BinaryPredicate;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
 import com.google.gerrit.extensions.api.groups.GroupInput;
@@ -613,40 +614,34 @@
   }
 
   private static Correspondence<AccountInfo, Account.Id> getAccountToIdCorrespondence() {
-    return new Correspondence<AccountInfo, Account.Id>() {
-      @Override
-      public boolean compare(AccountInfo actualAccount, Account.Id expectedId) {
-        Account.Id accountId =
-            Optional.ofNullable(actualAccount)
-                .map(account -> account._accountId)
-                .map(Account.Id::new)
-                .orElse(null);
-        return Objects.equals(accountId, expectedId);
-      }
-
-      @Override
-      public String toString() {
-        return "has ID";
-      }
-    };
+    return Correspondence.from(
+        new BinaryPredicate<AccountInfo, Account.Id>() {
+          @Override
+          public boolean apply(AccountInfo actualAccount, Account.Id expectedId) {
+            Account.Id accountId =
+                Optional.ofNullable(actualAccount)
+                    .map(account -> account._accountId)
+                    .map(Account.Id::new)
+                    .orElse(null);
+            return Objects.equals(accountId, expectedId);
+          }
+        },
+        "has ID");
   }
 
   private static Correspondence<GroupInfo, AccountGroup.UUID> getGroupToUuidCorrespondence() {
-    return new Correspondence<GroupInfo, AccountGroup.UUID>() {
-      @Override
-      public boolean compare(GroupInfo actualGroup, AccountGroup.UUID expectedUuid) {
-        AccountGroup.UUID groupUuid =
-            Optional.ofNullable(actualGroup)
-                .map(group -> group.id)
-                .map(AccountGroup.UUID::new)
-                .orElse(null);
-        return Objects.equals(groupUuid, expectedUuid);
-      }
-
-      @Override
-      public String toString() {
-        return "has UUID";
-      }
-    };
+    return Correspondence.from(
+        new BinaryPredicate<GroupInfo, AccountGroup.UUID>() {
+          @Override
+          public boolean apply(GroupInfo actualGroup, AccountGroup.UUID expectedUuid) {
+            AccountGroup.UUID groupUuid =
+                Optional.ofNullable(actualGroup)
+                    .map(group -> group.id)
+                    .map(AccountGroup.UUID::new)
+                    .orElse(null);
+            return Objects.equals(groupUuid, expectedUuid);
+          }
+        },
+        "has UUID");
   }
 }
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImplTest.java
index ea0bb61..5cbed1b 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImplTest.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/request/RequestScopeOperationsImplTest.java
@@ -48,50 +48,50 @@
 
   @Test
   public void setApiUserToExistingUserById() throws Exception {
-    fastCheckCurrentUser(admin.getId());
-    AcceptanceTestRequestScope.Context oldCtx = requestScopeOperations.setApiUser(user.getId());
-    assertThat(oldCtx.getUser().getAccountId()).isEqualTo(admin.getId());
-    checkCurrentUser(user.getId());
+    fastCheckCurrentUser(admin.id());
+    AcceptanceTestRequestScope.Context oldCtx = requestScopeOperations.setApiUser(user.id());
+    assertThat(oldCtx.getUser().getAccountId()).isEqualTo(admin.id());
+    checkCurrentUser(user.id());
   }
 
   @Test
   public void setApiUserToExistingUserByTestAccount() throws Exception {
-    fastCheckCurrentUser(admin.getId());
+    fastCheckCurrentUser(admin.id());
     TestAccount testAccount =
         accountOperations.account(accountOperations.newAccount().username("tester").create()).get();
     AcceptanceTestRequestScope.Context oldCtx = requestScopeOperations.setApiUser(testAccount);
-    assertThat(oldCtx.getUser().getAccountId()).isEqualTo(admin.getId());
+    assertThat(oldCtx.getUser().getAccountId()).isEqualTo(admin.id());
     checkCurrentUser(testAccount.accountId());
   }
 
   @Test
   public void setApiUserToNonExistingUser() throws Exception {
-    fastCheckCurrentUser(admin.getId());
+    fastCheckCurrentUser(admin.id());
     try {
       requestScopeOperations.setApiUser(new Account.Id(sequences.nextAccountId()));
       assert_().fail("expected RuntimeException");
     } catch (RuntimeException e) {
       // Expected.
     }
-    checkCurrentUser(admin.getId());
+    checkCurrentUser(admin.id());
   }
 
   @Test
   public void resetCurrentApiUserClearsCachedState() throws Exception {
-    requestScopeOperations.setApiUser(user.getId());
+    requestScopeOperations.setApiUser(user.id());
     PropertyKey<String> key = PropertyKey.create();
     atrScope.get().getUser().put(key, "foo");
     assertThat(atrScope.get().getUser().get(key)).hasValue("foo");
 
     AcceptanceTestRequestScope.Context oldCtx = requestScopeOperations.resetCurrentApiUser();
-    checkCurrentUser(user.getId());
+    checkCurrentUser(user.id());
     assertThat(atrScope.get().getUser().get(key)).isEmpty();
     assertThat(oldCtx.getUser().get(key)).hasValue("foo");
   }
 
   @Test
   public void setApiUserAnonymousSetsAnonymousUser() throws Exception {
-    fastCheckCurrentUser(admin.getId());
+    fastCheckCurrentUser(admin.id());
     requestScopeOperations.setApiUserAnonymous();
     assertThat(userProvider.get()).isInstanceOf(AnonymousUser.class);
   }
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
index d37a583..8400d02 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
@@ -49,9 +49,9 @@
       case V6_6:
         return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.6.2";
       case V6_7:
-        return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.7.0";
+        return "docker.elastic.co/elasticsearch/elasticsearch-oss:6.7.1";
       case V7_0:
-        return "docker.elastic.co/elasticsearch/elasticsearch-oss:7.0.0-rc1";
+        return "docker.elastic.co/elasticsearch/elasticsearch-oss:7.0.0";
     }
     throw new IllegalStateException("No tests for version: " + version.name());
   }
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java
index 0ce66e8..f01138a 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticV6QueryProjectsTest.java
@@ -41,7 +41,7 @@
       return;
     }
 
-    container = ElasticContainer.createAndStart(ElasticVersion.V6_2);
+    container = ElasticContainer.createAndStart(ElasticVersion.V6_7);
     nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
   }
 
diff --git a/javatests/com/google/gerrit/gpg/BUILD b/javatests/com/google/gerrit/gpg/BUILD
index baf65b7..6edfa93 100644
--- a/javatests/com/google/gerrit/gpg/BUILD
+++ b/javatests/com/google/gerrit/gpg/BUILD
@@ -19,7 +19,6 @@
         "//java/com/google/gerrit/server/schema",
         "//java/com/google/gerrit/testing:gerrit-test-util",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib/bouncycastle:bcpg",
         "//lib/bouncycastle:bcpg-neverlink",
         "//lib/bouncycastle:bcprov",
diff --git a/javatests/com/google/gerrit/httpd/BUILD b/javatests/com/google/gerrit/httpd/BUILD
index fe76d54..0fbd922 100644
--- a/javatests/com/google/gerrit/httpd/BUILD
+++ b/javatests/com/google/gerrit/httpd/BUILD
@@ -15,7 +15,6 @@
         "//javatests/com/google/gerrit/util/http/testutil",
         "//lib:gson",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:jimfs",
         "//lib:junit",
         "//lib:servlet-api-3_1-without-neverlink",
diff --git a/javatests/com/google/gerrit/index/query/QueryBuilderTest.java b/javatests/com/google/gerrit/index/query/QueryBuilderTest.java
index 5de3c54..6a397dc 100644
--- a/javatests/com/google/gerrit/index/query/QueryBuilderTest.java
+++ b/javatests/com/google/gerrit/index/query/QueryBuilderTest.java
@@ -52,9 +52,9 @@
     }
   }
 
-  private static class TestQueryBuilder extends QueryBuilder<Object> {
+  private static class TestQueryBuilder extends QueryBuilder<Object, TestQueryBuilder> {
     TestQueryBuilder() {
-      super(new QueryBuilder.Definition<>(TestQueryBuilder.class));
+      super(new QueryBuilder.Definition<>(TestQueryBuilder.class), null);
     }
 
     @Operator
diff --git a/javatests/com/google/gerrit/mail/BUILD b/javatests/com/google/gerrit/mail/BUILD
index 2fd8f24..5cfa00c 100644
--- a/javatests/com/google/gerrit/mail/BUILD
+++ b/javatests/com/google/gerrit/mail/BUILD
@@ -24,7 +24,6 @@
         "//java/org/eclipse/jgit:server",
         "//lib:gson",
         "//lib:guava-retrying",
-        "//lib:gwtorm",
         "//lib/commons:codec",
         "//lib/guice",
         "//lib/jgit/org.eclipse.jgit:jgit",
diff --git a/javatests/com/google/gerrit/server/BUILD b/javatests/com/google/gerrit/server/BUILD
index 39fc2aa..321d1fd 100644
--- a/javatests/com/google/gerrit/server/BUILD
+++ b/javatests/com/google/gerrit/server/BUILD
@@ -36,6 +36,7 @@
         ":custom-truth-subjects",
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/extensions/common/testing:common-test-util",
         "//java/com/google/gerrit/git",
@@ -64,7 +65,6 @@
         "//lib:gson",
         "//lib:guava",
         "//lib:guava-retrying",
-        "//lib:gwtorm",
         "//lib:protobuf",
         "//lib/auto:auto-value",
         "//lib/auto:auto-value-annotations",
diff --git a/javatests/com/google/gerrit/server/cache/serialize/BUILD b/javatests/com/google/gerrit/server/cache/serialize/BUILD
index 92f33ad..a0d5ea6 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/BUILD
+++ b/javatests/com/google/gerrit/server/cache/serialize/BUILD
@@ -7,8 +7,8 @@
         "//java/com/google/gerrit/server/cache/serialize",
         "//java/com/google/gerrit/server/cache/testing",
         "//java/com/google/gerrit/testing:gerrit-test-util",
+        "//java/com/google/gwtorm",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib:junit",
         "//lib:protobuf",
         "//lib/auto:auto-value",
diff --git a/javatests/com/google/gerrit/server/config/ScheduleConfigTest.java b/javatests/com/google/gerrit/server/config/ScheduleConfigTest.java
index f0e9153..6926052 100644
--- a/javatests/com/google/gerrit/server/config/ScheduleConfigTest.java
+++ b/javatests/com/google/gerrit/server/config/ScheduleConfigTest.java
@@ -41,15 +41,18 @@
   @Test
   public void initialDelay() throws Exception {
     assertThat(initialDelay("11:00", "1h")).isEqualTo(ms(1, HOURS));
+    assertThat(initialDelay("11:00", "1 hour")).isEqualTo(ms(1, HOURS));
     assertThat(initialDelay("05:30", "1h")).isEqualTo(ms(30, MINUTES));
     assertThat(initialDelay("09:30", "1h")).isEqualTo(ms(30, MINUTES));
     assertThat(initialDelay("13:30", "1h")).isEqualTo(ms(30, MINUTES));
     assertThat(initialDelay("13:59", "1h")).isEqualTo(ms(59, MINUTES));
 
     assertThat(initialDelay("11:00", "1d")).isEqualTo(ms(1, HOURS));
+    assertThat(initialDelay("11:00", "1 day")).isEqualTo(ms(1, HOURS));
     assertThat(initialDelay("05:30", "1d")).isEqualTo(ms(19, HOURS) + ms(30, MINUTES));
 
     assertThat(initialDelay("11:00", "1w")).isEqualTo(ms(1, HOURS));
+    assertThat(initialDelay("11:00", "1 week")).isEqualTo(ms(1, HOURS));
     assertThat(initialDelay("05:30", "1w")).isEqualTo(ms(7, DAYS) - ms(4, HOURS) - ms(30, MINUTES));
 
     assertThat(initialDelay("Mon 11:00", "1w")).isEqualTo(ms(3, DAYS) + ms(1, HOURS));
@@ -200,6 +203,9 @@
 
     rc.setString("a", null, ScheduleConfig.KEY_STARTTIME, "0100");
     assertThat(ScheduleConfig.builder(rc, "a").buildSchedule()).isEmpty();
+
+    rc.setString("a", null, ScheduleConfig.KEY_STARTTIME, "1:00");
+    assertThat(ScheduleConfig.builder(rc, "a").buildSchedule()).isEmpty();
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/server/group/db/BUILD b/javatests/com/google/gerrit/server/group/db/BUILD
index d02fa1b..b4652c9 100644
--- a/javatests/com/google/gerrit/server/group/db/BUILD
+++ b/javatests/com/google/gerrit/server/group/db/BUILD
@@ -8,6 +8,7 @@
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/common/data/testing:common-data-test-util",
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/extensions/common/testing:common-test-util",
         "//java/com/google/gerrit/git",
@@ -19,7 +20,6 @@
         "//java/com/google/gerrit/testing:gerrit-test-util",
         "//java/com/google/gerrit/truth",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib/jgit/org.eclipse.jgit:jgit",
         "//lib/jgit/org.eclipse.jgit.junit:junit",
         "//lib/truth",
diff --git a/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java b/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java
index 55eb0a8..e1211d3 100644
--- a/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java
+++ b/javatests/com/google/gerrit/server/group/db/GroupNameNotesTest.java
@@ -27,6 +27,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.common.data.GroupReference;
 import com.google.gerrit.common.data.testing.GroupReferenceSubject;
+import com.google.gerrit.exceptions.DuplicateKeyException;
 import com.google.gerrit.extensions.common.CommitInfo;
 import com.google.gerrit.extensions.common.testing.CommitInfoSubject;
 import com.google.gerrit.git.RefUpdateUtil;
@@ -43,7 +44,6 @@
 import com.google.gerrit.testing.TestTimeUtil;
 import com.google.gerrit.truth.ListSubject;
 import com.google.gerrit.truth.OptionalSubject;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
@@ -129,7 +129,7 @@
     createGroup(groupUuid, groupName);
 
     AccountGroup.UUID anotherGroupUuid = new AccountGroup.UUID("AnotherGroup");
-    exception.expect(OrmDuplicateKeyException.class);
+    exception.expect(DuplicateKeyException.class);
     exception.expectMessage(groupName.get());
     GroupNameNotes.forNewGroup(allUsersName, repo, anotherGroupUuid, groupName);
   }
@@ -205,7 +205,7 @@
     AccountGroup.NameKey anotherGroupName = new AccountGroup.NameKey("admins");
     createGroup(anotherGroupUuid, anotherGroupName);
 
-    exception.expect(OrmDuplicateKeyException.class);
+    exception.expect(DuplicateKeyException.class);
     exception.expectMessage(anotherGroupName.get());
     GroupNameNotes.forRename(allUsersName, repo, groupUuid, groupName, anotherGroupName);
   }
@@ -253,7 +253,7 @@
     createGroup(anotherGroupUuid, anotherName);
 
     ImmutableList<CommitInfo> commitsAfterFurtherGroup = log();
-    assertThatCommits(commitsAfterFurtherGroup).containsAllIn(commitsAfterCreation);
+    assertThatCommits(commitsAfterFurtherGroup).containsAtLeastElementsIn(commitsAfterCreation);
     assertThatCommits(commitsAfterFurtherGroup).lastElement().isNotIn(commitsAfterCreation);
   }
 
@@ -266,7 +266,7 @@
     renameGroup(groupUuid, groupName, anotherName);
 
     ImmutableList<CommitInfo> commitsAfterRename = log();
-    assertThatCommits(commitsAfterRename).containsAllIn(commitsAfterCreation);
+    assertThatCommits(commitsAfterRename).containsAtLeastElementsIn(commitsAfterCreation);
     assertThatCommits(commitsAfterRename).lastElement().isNotIn(commitsAfterCreation);
   }
 
diff --git a/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java b/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java
index 7117485..fd23da3 100644
--- a/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java
+++ b/javatests/com/google/gerrit/server/index/change/ChangeIndexRewriterTest.java
@@ -68,7 +68,7 @@
   public void nonIndexPredicate() throws Exception {
     Predicate<ChangeData> in = parse("foo:a");
     Predicate<ChangeData> out = rewrite(in);
-    assertThat(AndChangeSource.class).isSameAs(out.getClass());
+    assertThat(AndChangeSource.class).isSameInstanceAs(out.getClass());
     assertThat(out.getChildren())
         .containsExactly(
             query(Predicate.or(ChangeStatusPredicate.open(), ChangeStatusPredicate.closed())), in)
@@ -85,7 +85,7 @@
   public void nonIndexPredicates() throws Exception {
     Predicate<ChangeData> in = parse("foo:a OR foo:b");
     Predicate<ChangeData> out = rewrite(in);
-    assertThat(AndChangeSource.class).isSameAs(out.getClass());
+    assertThat(AndChangeSource.class).isSameInstanceAs(out.getClass());
     assertThat(out.getChildren())
         .containsExactly(
             query(Predicate.or(ChangeStatusPredicate.open(), ChangeStatusPredicate.closed())), in)
@@ -96,7 +96,7 @@
   public void oneIndexPredicate() throws Exception {
     Predicate<ChangeData> in = parse("foo:a file:b");
     Predicate<ChangeData> out = rewrite(in);
-    assertThat(AndChangeSource.class).isSameAs(out.getClass());
+    assertThat(AndChangeSource.class).isSameInstanceAs(out.getClass());
     assertThat(out.getChildren()).containsExactly(query(in.getChild(1)), in.getChild(0)).inOrder();
   }
 
@@ -110,7 +110,7 @@
   public void threeLevelTreeWithSomeIndexPredicates() throws Exception {
     Predicate<ChangeData> in = parse("-foo:a (file:b OR file:c)");
     Predicate<ChangeData> out = rewrite(in);
-    assertThat(out.getClass()).isSameAs(AndChangeSource.class);
+    assertThat(out.getClass()).isSameInstanceAs(AndChangeSource.class);
     assertThat(out.getChildren()).containsExactly(query(in.getChild(1)), in.getChild(0)).inOrder();
   }
 
@@ -118,7 +118,7 @@
   public void multipleIndexPredicates() throws Exception {
     Predicate<ChangeData> in = parse("file:a OR foo:b OR file:c OR foo:d");
     Predicate<ChangeData> out = rewrite(in);
-    assertThat(out.getClass()).isSameAs(OrSource.class);
+    assertThat(out.getClass()).isSameInstanceAs(OrSource.class);
     assertThat(out.getChildren())
         .containsExactly(query(or(in.getChild(0), in.getChild(2))), in.getChild(1), in.getChild(3))
         .inOrder();
@@ -128,7 +128,7 @@
   public void indexAndNonIndexPredicates() throws Exception {
     Predicate<ChangeData> in = parse("status:new bar:p file:a");
     Predicate<ChangeData> out = rewrite(in);
-    assertThat(AndChangeSource.class).isSameAs(out.getClass());
+    assertThat(AndChangeSource.class).isSameInstanceAs(out.getClass());
     assertThat(out.getChildren())
         .containsExactly(query(and(in.getChild(0), in.getChild(2))), in.getChild(1))
         .inOrder();
diff --git a/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java b/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
index efe6a5a..34c5717 100644
--- a/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
+++ b/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeDataSource;
-import com.google.gwtorm.server.OrmException;
 import org.junit.Ignore;
 
 @Ignore
@@ -52,12 +51,12 @@
     }
 
     @Override
-    public ResultSet<ChangeData> read() throws OrmException {
+    public ResultSet<ChangeData> read() {
       throw new UnsupportedOperationException();
     }
 
     @Override
-    public ResultSet<FieldBundle> readRaw() throws OrmException {
+    public ResultSet<FieldBundle> readRaw() {
       throw new UnsupportedOperationException("not implemented");
     }
 
diff --git a/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java b/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java
index 7a5ed41..0753127 100644
--- a/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java
+++ b/javatests/com/google/gerrit/server/index/change/FakeQueryBuilder.java
@@ -24,7 +24,7 @@
 public class FakeQueryBuilder extends ChangeQueryBuilder {
   FakeQueryBuilder(ChangeIndexCollection indexes) {
     super(
-        new FakeQueryBuilder.Definition<>(FakeQueryBuilder.class),
+        new ChangeQueryBuilder.Definition<>(FakeQueryBuilder.class),
         new ChangeQueryBuilder.Arguments(
             null, null, null, null, null, null, null, null, null, null, null, null, null, null,
             null, null, null, null, indexes, null, null, null, null, null, null, null));
diff --git a/javatests/com/google/gerrit/server/mail/send/CommentSenderTest.java b/javatests/com/google/gerrit/server/mail/send/CommentSenderTest.java
index 87e98fd..0682bb3 100644
--- a/javatests/com/google/gerrit/server/mail/send/CommentSenderTest.java
+++ b/javatests/com/google/gerrit/server/mail/send/CommentSenderTest.java
@@ -17,13 +17,12 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.gerrit.testing.GerritBaseTests;
-import com.google.gwtorm.server.OrmException;
 import java.util.Collections;
 import org.junit.Test;
 
 public class CommentSenderTest extends GerritBaseTests {
   private static class TestSender extends CommentSender {
-    TestSender() throws OrmException {
+    TestSender() {
       super(null, null, null, null, null);
     }
   }
diff --git a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
index 712449e..a1f318f 100644
--- a/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/AbstractChangeNotesTest.java
@@ -57,7 +57,6 @@
 import com.google.gerrit.testing.InMemoryRepositoryManager;
 import com.google.gerrit.testing.TestChanges;
 import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Guice;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
@@ -213,7 +212,7 @@
     return update;
   }
 
-  protected ChangeNotes newNotes(Change c) throws OrmException {
+  protected ChangeNotes newNotes(Change c) {
     return new ChangeNotes(args, c, true, null).load();
   }
 
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
index 1da4121..a6c0224 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -35,6 +35,7 @@
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.mail.Address;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Branch;
@@ -54,7 +55,6 @@
 import com.google.gerrit.server.notedb.ChangeNotesCommit.ChangeNotesRevWalk;
 import com.google.gerrit.server.util.time.TimeUtil;
 import com.google.gerrit.testing.TestChanges;
-import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import java.sql.Timestamp;
 import java.util.LinkedHashSet;
@@ -993,7 +993,7 @@
     try {
       newNotes(c);
       fail("Expected IOException");
-    } catch (OrmException e) {
+    } catch (StorageException e) {
       assertCause(
           e,
           ConfigInvalidException.class,
@@ -3016,7 +3016,7 @@
     Change c = newChange();
     ChangeUpdate update = newUpdate(c, changeOwner);
     update.setRevertOf(newChange().getId().get());
-    exception.expect(OrmException.class);
+    exception.expect(StorageException.class);
     exception.expectMessage("Given ChangeUpdate is only allowed on initial commit");
     update.commit();
   }
diff --git a/javatests/com/google/gerrit/server/notedb/IntBlobTest.java b/javatests/com/google/gerrit/server/notedb/IntBlobTest.java
index 1abaa22..1cbe61d 100644
--- a/javatests/com/google/gerrit/server/notedb/IntBlobTest.java
+++ b/javatests/com/google/gerrit/server/notedb/IntBlobTest.java
@@ -18,10 +18,10 @@
 import static com.google.common.truth.Truth.assert_;
 import static com.google.gerrit.truth.OptionalSubject.assertThat;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.git.LockFailureException;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
@@ -87,8 +87,8 @@
     ObjectId id = tr.update(refName, tr.blob("1 2 3"));
     try {
       IntBlob.parse(repo, refName);
-      assert_().fail("Expected OrmException");
-    } catch (OrmException e) {
+      assert_().fail("Expected StorageException");
+    } catch (StorageException e) {
       assertThat(e).hasMessageThat().isEqualTo("invalid value in refs/foo blob at " + id.name());
     }
   }
diff --git a/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java b/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java
index 5aad6ff..263873d 100644
--- a/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java
+++ b/javatests/com/google/gerrit/server/notedb/RepoSequenceTest.java
@@ -23,12 +23,12 @@
 import com.github.rholder.retry.RetryerBuilder;
 import com.github.rholder.retry.StopStrategies;
 import com.google.common.util.concurrent.Runnables;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.testing.GerritBaseTests;
 import com.google.gerrit.testing.InMemoryRepositoryManager;
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -67,7 +67,7 @@
       for (int i = 1; i <= max; i++) {
         try {
           assertThat(s.next()).named("i=" + i + " for " + name).isEqualTo(i);
-        } catch (OrmException e) {
+        } catch (StorageException e) {
           throw new AssertionError("failed batchSize=" + batchSize + ", i=" + i, e);
         }
       }
@@ -168,7 +168,7 @@
   @Test
   public void failOnInvalidValue() throws Exception {
     ObjectId id = writeBlob("id", "not a number");
-    exception.expect(OrmException.class);
+    exception.expect(StorageException.class);
     exception.expectMessage("invalid value in refs/sequences/id blob at " + id.name());
     newSequence("id", 1, 3).next();
   }
@@ -181,7 +181,7 @@
       try {
         newSequence("id", 1, 3).next();
         fail();
-      } catch (OrmException e) {
+      } catch (StorageException e) {
         assertThat(e.getCause()).isInstanceOf(ExecutionException.class);
         assertThat(e.getCause().getCause()).isInstanceOf(IncorrectObjectTypeException.class);
       }
@@ -200,7 +200,7 @@
             RetryerBuilder.<RefUpdate>newBuilder()
                 .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                 .build());
-    exception.expect(OrmException.class);
+    exception.expect(StorageException.class);
     exception.expectMessage("Failed to update refs/sequences/id: LOCK_FAILURE");
     s.next();
   }
@@ -335,7 +335,7 @@
             RetryerBuilder.<RefUpdate>newBuilder()
                 .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                 .build());
-    exception.expect(OrmException.class);
+    exception.expect(StorageException.class);
     exception.expectMessage("Failed to update refs/sequences/id: LOCK_FAILURE");
     s.increaseTo(2);
   }
diff --git a/javatests/com/google/gerrit/server/permissions/RefControlTest.java b/javatests/com/google/gerrit/server/permissions/RefControlTest.java
index 3a9037c..5f08875 100644
--- a/javatests/com/google/gerrit/server/permissions/RefControlTest.java
+++ b/javatests/com/google/gerrit/server/permissions/RefControlTest.java
@@ -41,7 +41,7 @@
 import com.google.gerrit.common.data.LabelType;
 import com.google.gerrit.common.data.PermissionRange;
 import com.google.gerrit.common.data.PermissionRule;
-import com.google.gerrit.common.errors.InvalidNameException;
+import com.google.gerrit.exceptions.InvalidNameException;
 import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
 import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.reviewdb.client.AccountGroup;
diff --git a/javatests/com/google/gerrit/server/query/change/BUILD b/javatests/com/google/gerrit/server/query/change/BUILD
index 1ccac32..e8a63b8 100644
--- a/javatests/com/google/gerrit/server/query/change/BUILD
+++ b/javatests/com/google/gerrit/server/query/change/BUILD
@@ -21,7 +21,6 @@
         "//java/com/google/gerrit/server/util/time",
         "//java/com/google/gerrit/testing:gerrit-test-util",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib/guice",
         "//lib/jgit/org.eclipse.jgit:jgit",
         "//lib/jgit/org.eclipse.jgit.junit:junit",
@@ -43,7 +42,6 @@
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/testing:gerrit-test-util",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib/guice",
         "//lib/jgit/org.eclipse.jgit:jgit",
         "//lib/jgit/org.eclipse.jgit.junit:junit",
@@ -60,6 +58,7 @@
     ),
     visibility = ["//visibility:public"],
     deps = [
+        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/proto/testing",
         "//java/com/google/gerrit/reviewdb:server",
@@ -67,7 +66,6 @@
         "//java/com/google/gerrit/server/cache/testing",
         "//java/com/google/gerrit/testing:gerrit-test-util",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib/jgit/org.eclipse.jgit:jgit",
         "//lib/truth",
         "//lib/truth:truth-proto-extension",
diff --git a/javatests/com/google/gerrit/server/query/change/ChangeDataTest.java b/javatests/com/google/gerrit/server/query/change/ChangeDataTest.java
index f7252c0..aba0018 100644
--- a/javatests/com/google/gerrit/server/query/change/ChangeDataTest.java
+++ b/javatests/com/google/gerrit/server/query/change/ChangeDataTest.java
@@ -37,6 +37,6 @@
     PatchSet ps2 = new PatchSet(new PatchSet.Id(cd.getId(), currId + 2));
     cd.setPatchSets(ImmutableList.of(ps1, ps2));
     PatchSet curr2 = cd.currentPatchSet();
-    assertThat(curr2).isNotSameAs(curr1);
+    assertThat(curr2).isNotSameInstanceAs(curr1);
   }
 }
diff --git a/javatests/com/google/gerrit/server/query/change/RegexPathPredicateTest.java b/javatests/com/google/gerrit/server/query/change/RegexPathPredicateTest.java
index 9944a42..135e9c26 100644
--- a/javatests/com/google/gerrit/server/query/change/RegexPathPredicateTest.java
+++ b/javatests/com/google/gerrit/server/query/change/RegexPathPredicateTest.java
@@ -20,13 +20,12 @@
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.testing.GerritBaseTests;
-import com.google.gwtorm.server.OrmException;
 import java.util.Arrays;
 import org.junit.Test;
 
 public class RegexPathPredicateTest extends GerritBaseTests {
   @Test
-  public void prefixOnlyOptimization() throws OrmException {
+  public void prefixOnlyOptimization() {
     RegexPathPredicate p = predicate("^a/b/.*");
     assertTrue(p.match(change("a/b/source.c")));
     assertFalse(p.match(change("source.c")));
@@ -36,7 +35,7 @@
   }
 
   @Test
-  public void prefixReducesSearchSpace() throws OrmException {
+  public void prefixReducesSearchSpace() {
     RegexPathPredicate p = predicate("^a/b/.*\\.[ch]");
     assertTrue(p.match(change("a/b/source.c")));
     assertFalse(p.match(change("a/b/source.res")));
@@ -46,7 +45,7 @@
   }
 
   @Test
-  public void fileExtension_Constant() throws OrmException {
+  public void fileExtension_Constant() {
     RegexPathPredicate p = predicate("^.*\\.res");
     assertTrue(p.match(change("test.res")));
     assertTrue(p.match(change("foo/bar/test.res")));
@@ -54,7 +53,7 @@
   }
 
   @Test
-  public void fileExtension_CharacterGroup() throws OrmException {
+  public void fileExtension_CharacterGroup() {
     RegexPathPredicate p = predicate("^.*\\.[ch]");
     assertTrue(p.match(change("test.c")));
     assertTrue(p.match(change("test.h")));
@@ -62,7 +61,7 @@
   }
 
   @Test
-  public void endOfString() throws OrmException {
+  public void endOfString() {
     assertTrue(predicate("^a$").match(change("a")));
     assertFalse(predicate("^a$").match(change("a$")));
 
@@ -71,7 +70,7 @@
   }
 
   @Test
-  public void exactMatch() throws OrmException {
+  public void exactMatch() {
     RegexPathPredicate p = predicate("^foo.c");
     assertTrue(p.match(change("foo.c")));
     assertFalse(p.match(change("foo.cc")));
@@ -82,7 +81,7 @@
     return new RegexPathPredicate(pattern);
   }
 
-  private static ChangeData change(String... files) throws OrmException {
+  private static ChangeData change(String... files) {
     Arrays.sort(files);
     ChangeData cd = ChangeData.createForTest(new Project.NameKey("project"), new Change.Id(1), 1);
     cd.setCurrentFilePaths(Arrays.asList(files));
diff --git a/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java b/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java
index b23219b..bbbcff6 100644
--- a/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java
+++ b/javatests/com/google/gerrit/server/schema/NoteDbSchemaUpdaterTest.java
@@ -22,6 +22,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSortedMap;
 import com.google.common.collect.ImmutableSortedSet;
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.AllUsersName;
@@ -33,7 +34,6 @@
 import com.google.gerrit.testing.GerritBaseTests;
 import com.google.gerrit.testing.InMemoryRepositoryManager;
 import com.google.gerrit.testing.TestUpdateUI;
-import com.google.gwtorm.server.OrmException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -64,8 +64,8 @@
   public void downgradeNotSupported() throws Exception {
     try {
       requiredUpgrades(14, versions(10, 11, 12, 13));
-      assert_().fail("expected OrmException");
-    } catch (OrmException e) {
+      assert_().fail("expected StorageException");
+    } catch (StorageException e) {
       assertThat(e)
           .hasMessageThat()
           .contains("Cannot downgrade NoteDb schema from version 14 to 13");
@@ -78,8 +78,8 @@
     assertThat(requiredUpgrades(9, versions)).containsExactly(10, 11, 12).inOrder();
     try {
       requiredUpgrades(8, versions);
-      assert_().fail("expected OrmException");
-    } catch (OrmException e) {
+      assert_().fail("expected StorageException");
+    } catch (StorageException e) {
       assertThat(e).hasMessageThat().contains("Cannot skip NoteDb schema from version 8 to 10");
     }
   }
@@ -122,21 +122,21 @@
       }
 
       @Override
-      public void create() throws OrmException, IOException {
+      public void create() throws IOException {
         try (Repository repo = repoManager.createRepository(allProjectsName)) {
           if (initialVersion.isPresent()) {
             TestRepository<?> tr = new TestRepository<>(repo);
             tr.update(RefNames.REFS_VERSION, tr.blob(initialVersion.get().toString()));
           }
         } catch (Exception e) {
-          throw new OrmException(e);
+          throw new StorageException(e);
         }
         repoManager.createRepository(allUsersName).close();
         setUp();
       }
 
       @Override
-      public void ensureCreated() throws OrmException, IOException {
+      public void ensureCreated() throws IOException {
         try {
           repoManager.openRepository(allProjectsName).close();
         } catch (RepositoryNotFoundException e) {
@@ -152,7 +152,7 @@
       cfg.setString("noteDb", "changes", "disableReviewDb", "true");
     }
 
-    protected void seedGroupSequenceRef() throws OrmException {
+    protected void seedGroupSequenceRef() {
       new RepoSequence(
               repoManager,
               GitReferenceUpdated.DISABLED,
@@ -163,12 +163,8 @@
           .next();
     }
 
-    /**
-     * Test-specific setup.
-     *
-     * @throws OrmException if an error occurs.
-     */
-    protected void setUp() throws OrmException {}
+    /** Test-specific setup. */
+    protected void setUp() {}
 
     ImmutableList<String> update() throws Exception {
       updater.update(
@@ -211,7 +207,7 @@
     TestUpdate u =
         new TestUpdate(Optional.empty()) {
           @Override
-          public void setUp() throws OrmException {
+          public void setUp() {
             setNotesMigrationConfig();
             seedGroupSequenceRef();
           }
@@ -231,14 +227,14 @@
     TestUpdate u =
         new TestUpdate(Optional.empty()) {
           @Override
-          public void setUp() throws OrmException {
+          public void setUp() {
             seedGroupSequenceRef();
           }
         };
     try {
       u.update();
-      assert_().fail("expected OrmException");
-    } catch (OrmException e) {
+      assert_().fail("expected StorageException");
+    } catch (StorageException e) {
       assertThat(e).hasMessageThat().contains("NoteDb change migration was not completed");
     }
     assertThat(u.getMessages()).isEmpty();
@@ -256,8 +252,8 @@
         };
     try {
       u.update();
-      assert_().fail("expected OrmException");
-    } catch (OrmException e) {
+      assert_().fail("expected StorageException");
+    } catch (StorageException e) {
       assertThat(e).hasMessageThat().contains("upgrade to 2.16.x first");
     }
     assertThat(u.getMessages()).isEmpty();
diff --git a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java
index a0176de..9c62d7f 100644
--- a/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java
+++ b/javatests/com/google/gerrit/server/schema/NoteDbSchemaVersionManagerTest.java
@@ -18,11 +18,11 @@
 import static com.google.common.truth.Truth.assert_;
 import static com.google.gerrit.reviewdb.client.RefNames.REFS_VERSION;
 
+import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.testing.GerritBaseTests;
 import com.google.gerrit.testing.InMemoryRepositoryManager;
-import com.google.gwtorm.server.OrmException;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.ObjectId;
 import org.junit.Before;
@@ -57,8 +57,8 @@
     tr.update(REFS_VERSION, blobId);
     try {
       manager.read();
-      assert_().fail("expected OrmException");
-    } catch (OrmException e) {
+      assert_().fail("expected StorageException");
+    } catch (StorageException e) {
       assertThat(e)
           .hasMessageThat()
           .isEqualTo("invalid value in refs/meta/version blob at " + blobId.name());
@@ -83,8 +83,8 @@
     tr.update(REFS_VERSION, tr.blob("123"));
     try {
       manager.increment(456);
-      assert_().fail("expected OrmException");
-    } catch (OrmException e) {
+      assert_().fail("expected StorageException");
+    } catch (StorageException e) {
       assertThat(e)
           .hasMessageThat()
           .isEqualTo("Expected old version 456 for refs/meta/version, found 123");
diff --git a/javatests/com/google/gerrit/server/update/BUILD b/javatests/com/google/gerrit/server/update/BUILD
index 9117e85..efe24d5 100644
--- a/javatests/com/google/gerrit/server/update/BUILD
+++ b/javatests/com/google/gerrit/server/update/BUILD
@@ -13,7 +13,6 @@
         "//java/com/google/gerrit/server/util/time",
         "//java/com/google/gerrit/testing:gerrit-test-util",
         "//lib:guava",
-        "//lib:gwtorm",
         "//lib/guice",
         "//lib/jgit/org.eclipse.jgit:jgit",
         "//lib/jgit/org.eclipse.jgit.junit:junit",
diff --git a/javatests/com/google/gerrit/server/update/BatchUpdateTest.java b/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
index 2ae0a5f..cca844e 100644
--- a/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
+++ b/javatests/com/google/gerrit/server/update/BatchUpdateTest.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.server.update;
 
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
 
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.CurrentUser;
@@ -51,8 +51,8 @@
 
   @Test
   public void addRefUpdateFromFastForwardCommit() throws Exception {
-    final RevCommit masterCommit = repo.branch("master").commit().create();
-    final RevCommit branchCommit = repo.branch("branch").commit().parent(masterCommit).create();
+    RevCommit masterCommit = repo.branch("master").commit().create();
+    RevCommit branchCommit = repo.branch("branch").commit().parent(masterCommit).create();
 
     try (BatchUpdate bu = batchUpdateFactory.create(project, user.get(), TimeUtil.nowTs())) {
       bu.addRepoOnlyOp(
@@ -65,7 +65,7 @@
       bu.execute();
     }
 
-    assertEquals(
-        repo.getRepository().exactRef("refs/heads/master").getObjectId(), branchCommit.getId());
+    assertThat(repo.getRepository().exactRef("refs/heads/master").getObjectId())
+        .isEqualTo(branchCommit.getId());
   }
 }
diff --git a/javatests/com/google/gerrit/server/util/git/BUILD b/javatests/com/google/gerrit/server/util/git/BUILD
index 096afa6..cdc823e 100644
--- a/javatests/com/google/gerrit/server/util/git/BUILD
+++ b/javatests/com/google/gerrit/server/util/git/BUILD
@@ -16,7 +16,6 @@
         "//lib:gson",
         "//lib:guava",
         "//lib:guava-retrying",
-        "//lib:gwtorm",
         "//lib:protobuf",
         "//lib/auto:auto-value",
         "//lib/auto:auto-value-annotations",
diff --git a/lib/BUILD b/lib/BUILD
index 8221e13..9dce60a 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -34,20 +34,6 @@
 )
 
 java_library(
-    name = "gwtorm-client",
-    data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
-    exports = ["@gwtorm-client//jar"],
-)
-
-java_library(
-    name = "gwtorm-client_src",
-    data = ["//lib:LICENSE-Apache2.0"],
-    visibility = ["//visibility:public"],
-    exports = ["@gwtorm-client//jar:src"],
-)
-
-java_library(
     name = "protobuf",
     data = ["//lib:LICENSE-protobuf"],
     visibility = ["//visibility:public"],
@@ -55,17 +41,6 @@
 )
 
 java_library(
-    name = "gwtorm",
-    visibility = ["//visibility:public"],
-    exports = [":gwtorm-client"],
-    runtime_deps = [
-        ":protobuf",
-        "//lib/antlr:java-runtime",
-        "//lib/ow2:ow2-asm",
-    ],
-)
-
-java_library(
     name = "guava-failureaccess",
     data = ["//lib:LICENSE-Apache2.0"],
     visibility = ["//visibility:public"],
diff --git a/lib/js/npm.bzl b/lib/js/npm.bzl
index 92f44bd..8a9e1ee 100644
--- a/lib/js/npm.bzl
+++ b/lib/js/npm.bzl
@@ -1,11 +1,11 @@
 NPM_VERSIONS = {
-    "bower": "1.8.2",
+    "bower": "1.8.8",
     "crisper": "2.0.2",
     "polymer-bundler": "4.0.2",
 }
 
 NPM_SHA1S = {
-    "bower": "adf53529c8d4af02ef24fb8d5341c1419d33e2f7",
+    "bower": "82544be34a33aeae7efb8bdf9905247b2cffa985",
     "crisper": "7183c58cea33632fb036c91cefd1b43e390d22a2",
     "polymer-bundler": "6b296b6099ab5a0e93ca914cbe93e753f2395910",
 }
diff --git a/lib/zlib/BUILD b/lib/zlib/BUILD
new file mode 100644
index 0000000..f948117
--- /dev/null
+++ b/lib/zlib/BUILD
@@ -0,0 +1,60 @@
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])  # BSD/MIT-like license (for zlib)
+
+_ZLIB_HEADERS = [
+    "crc32.h",
+    "deflate.h",
+    "gzguts.h",
+    "inffast.h",
+    "inffixed.h",
+    "inflate.h",
+    "inftrees.h",
+    "trees.h",
+    "zconf.h",
+    "zlib.h",
+    "zutil.h",
+]
+
+_ZLIB_PREFIXED_HEADERS = ["zlib/include/" + hdr for hdr in _ZLIB_HEADERS]
+
+# In order to limit the damage from the `includes` propagation
+# via `:zlib`, copy the public headers to a subdirectory and
+# expose those.
+genrule(
+    name = "copy_public_headers",
+    srcs = _ZLIB_HEADERS,
+    outs = _ZLIB_PREFIXED_HEADERS,
+    cmd = "cp $(SRCS) $(@D)/zlib/include/",
+    visibility = ["//visibility:private"],
+)
+
+cc_library(
+    name = "zlib",
+    srcs = [
+        "adler32.c",
+        "compress.c",
+        "crc32.c",
+        "deflate.c",
+        "gzclose.c",
+        "gzlib.c",
+        "gzread.c",
+        "gzwrite.c",
+        "infback.c",
+        "inffast.c",
+        "inflate.c",
+        "inftrees.c",
+        "trees.c",
+        "uncompr.c",
+        "zutil.c",
+        # Include the un-prefixed headers in srcs to work
+        # around the fact that zlib isn't consistent in its
+        # choice of <> or "" delimiter when including itself.
+    ] + _ZLIB_HEADERS,
+    hdrs = _ZLIB_PREFIXED_HEADERS,
+    copts = [
+        "-Wno-unused-variable",
+        "-Wno-implicit-function-declaration",
+    ],
+    includes = ["zlib/include/"],
+)
diff --git a/plugins/BUILD b/plugins/BUILD
index 5e5dbbf..8baa3c7 100644
--- a/plugins/BUILD
+++ b/plugins/BUILD
@@ -31,6 +31,7 @@
     "//antlr3:query_parser",
     "//java/com/google/gerrit/common:annotations",
     "//java/com/google/gerrit/common:server",
+    "//java/com/google/gerrit/exceptions",
     "//java/com/google/gerrit/extensions:api",
     "//java/com/google/gerrit/git",
     "//java/com/google/gerrit/index",
@@ -42,6 +43,7 @@
     "//java/com/google/gerrit/metrics",
     "//java/com/google/gerrit/metrics/dropwizard",
     "//java/com/google/gerrit/reviewdb:server",
+    "//java/com/google/gerrit/server/api",
     "//java/com/google/gerrit/server/audit",
     "//java/com/google/gerrit/server/cache/mem",
     "//java/com/google/gerrit/server/logging",
@@ -49,6 +51,8 @@
     "//java/com/google/gerrit/server/util/time",
     "//java/com/google/gerrit/util/cli",
     "//java/com/google/gerrit/util/http",
+    "//lib/antlr:java-runtime",
+    "//lib/auto:auto-value-annotations",
     "//lib/commons:compress",
     "//lib/commons:dbcp",
     "//lib/commons:lang",
@@ -75,7 +79,6 @@
     "//lib:guava",
     "//lib:guava-retrying",
     "//lib:gson",
-    "//lib:gwtorm",
     "//lib:icu4j",
     "//lib:jsch",
     "//lib:mime-util",
diff --git a/plugins/codemirror-editor b/plugins/codemirror-editor
index 65f3cba..3baf62e 160000
--- a/plugins/codemirror-editor
+++ b/plugins/codemirror-editor
@@ -1 +1 @@
-Subproject commit 65f3cbac2174c7aad65a3d7c9325a66871ae5752
+Subproject commit 3baf62e1f12bea107598777a3881455cf39fd8ab
diff --git a/plugins/delete-project b/plugins/delete-project
index 1eb523b..4401de0 160000
--- a/plugins/delete-project
+++ b/plugins/delete-project
@@ -1 +1 @@
-Subproject commit 1eb523bfc94c9744575b276f9a246578619a7a83
+Subproject commit 4401de09767d6e8655d29377f95145840a73feaf
diff --git a/plugins/replication b/plugins/replication
index c0a25b0..6a4105d 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit c0a25b0803252e6eca5997766e70cbe5edbc90d6
+Subproject commit 6a4105d7f3dc09fe2eb7cb15901178eab7723a44
diff --git a/plugins/reviewnotes b/plugins/reviewnotes
index d7ba499..3dec016 160000
--- a/plugins/reviewnotes
+++ b/plugins/reviewnotes
@@ -1 +1 @@
-Subproject commit d7ba4997b5c0038c85f6393b73a9ca16ca4fb927
+Subproject commit 3dec016f9186131b2359d689a3e4a2c14717710e
diff --git a/plugins/singleusergroup b/plugins/singleusergroup
index ff77708..25bcffa 160000
--- a/plugins/singleusergroup
+++ b/plugins/singleusergroup
@@ -1 +1 @@
-Subproject commit ff777080a881e85060ab446ae3f00612bcfb5ae1
+Subproject commit 25bcffa8e0a35df7f62b70b7dd47d2284f3a777a
diff --git a/plugins/webhooks b/plugins/webhooks
index cce7041..93bcec5 160000
--- a/plugins/webhooks
+++ b/plugins/webhooks
@@ -1 +1 @@
-Subproject commit cce7041e3f738259f2e75a928b53e7e448a3e11c
+Subproject commit 93bcec540df35f0277240bc63bf5cc5f306e4ddd
diff --git a/polygerrit-ui/app/BUILD b/polygerrit-ui/app/BUILD
index bba0640..19bdcee 100644
--- a/polygerrit-ui/app/BUILD
+++ b/polygerrit-ui/app/BUILD
@@ -51,6 +51,7 @@
         [
             "bower_components/**/*.html",
             "bower_components/**/*.js",
+            "bower_components/**/*.map",
         ],
     ),
 )
diff --git a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
index 4b670da..c21e96f 100644
--- a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
@@ -52,6 +52,7 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
+        _legacyUndefinedCheck: true,
         behaviors: [
           Gerrit.BaseUrlBehavior,
         ],
diff --git a/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html b/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html
index e92eb49..96d4a08 100644
--- a/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html
@@ -38,6 +38,7 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'docs-url-behavior-element',
+        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.DocsUrlBehavior],
       });
     });
diff --git a/polygerrit-ui/app/behaviors/dom-util-behavior/dom-util-behavior_test.html b/polygerrit-ui/app/behaviors/dom-util-behavior/dom-util-behavior_test.html
index 640d902..e445a78 100644
--- a/polygerrit-ui/app/behaviors/dom-util-behavior/dom-util-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/dom-util-behavior/dom-util-behavior_test.html
@@ -46,6 +46,7 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
+        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.DomUtilBehavior],
       });
     });
diff --git a/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior_test.html b/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior_test.html
index 929834f..0b37a0d 100644
--- a/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-access-behavior/gr-access-behavior_test.html
@@ -38,6 +38,7 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
+        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.AccessBehavior],
       });
     });
diff --git a/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.html b/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.html
index a1902cd..7c23c00 100644
--- a/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior_test.html
@@ -41,6 +41,7 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
+        _legacyUndefinedCheck: true,
         behaviors: [
           Gerrit.AdminNavBehavior,
         ],
diff --git a/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html b/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html
index 3ac94fe..820d6bc 100644
--- a/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html
@@ -44,6 +44,7 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element-anon',
+        _legacyUndefinedCheck: true,
         behaviors: [
           Gerrit.AnonymousNameBehavior,
         ],
diff --git a/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html b/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html
index e8cbb09..b052d06 100644
--- a/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html
@@ -48,6 +48,7 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
+        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.ChangeTableBehavior],
       });
     });
diff --git a/polygerrit-ui/app/behaviors/gr-list-view-behavior/gr-list-view-behavior_test.html b/polygerrit-ui/app/behaviors/gr-list-view-behavior/gr-list-view-behavior_test.html
index 54b979f..f6c765f 100644
--- a/polygerrit-ui/app/behaviors/gr-list-view-behavior/gr-list-view-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-list-view-behavior/gr-list-view-behavior_test.html
@@ -40,6 +40,7 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
+        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.ListViewBehavior],
       });
     });
diff --git a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
index 8bca339..943e000 100644
--- a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
@@ -51,6 +51,7 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'tooltip-behavior-element',
+        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.TooltipBehavior],
       });
     });
diff --git a/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.html b/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.html
index 73dda1b..d909e86 100644
--- a/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-url-encoding-behavior/gr-url-encoding-behavior_test.html
@@ -40,6 +40,7 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
+        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.URLEncodingBehavior],
       });
     });
diff --git a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
index dac90f8..d013299 100644
--- a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
@@ -50,6 +50,7 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
+        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.KeyboardShortcutBehavior],
         keyBindings: {
           k: '_handleKey',
diff --git a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
index 49d90f0..6af43dc 100644
--- a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
@@ -54,6 +54,7 @@
       // Define a Polymer element that uses this behavior.
       Polymer({
         is: 'test-element',
+        _legacyUndefinedCheck: true,
         behaviors: [
           Gerrit.BaseUrlBehavior,
           Gerrit.RESTClientBehavior,
diff --git a/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html b/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html
index bc16b39..6e040a3 100644
--- a/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/safe-types-behavior/safe-types-behavior_test.html
@@ -39,6 +39,7 @@
     suiteSetup(() => {
       Polymer({
         is: 'safe-types-element',
+        _legacyUndefinedCheck: true,
         behaviors: [Gerrit.SafeTypes],
       });
     });
diff --git a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.js b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.js
index 6fb7b0e..6adf127 100644
--- a/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.js
+++ b/polygerrit-ui/app/elements/admin/gr-access-section/gr-access-section.js
@@ -38,6 +38,7 @@
 
   Polymer({
     is: 'gr-access-section',
+    _legacyUndefinedCheck: true,
 
     properties: {
       capabilities: Object,
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js
index 1f5457a..aca691a 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-admin-group-list',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
index 3d430c2..4d7fd0c 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.js
@@ -22,6 +22,7 @@
 
   Polymer({
     is: 'gr-admin-view',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /** @type {?} */
diff --git a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js
index b5dcf63..9c0e405 100644
--- a/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.js
@@ -25,6 +25,7 @@
 
   Polymer({
     is: 'gr-confirm-delete-item-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js
index 8e15755..7eec959 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.js
@@ -22,6 +22,7 @@
 
   Polymer({
     is: 'gr-create-change-dialog',
+    _legacyUndefinedCheck: true,
 
     properties: {
       repoName: String,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.js
index 01aeb43..ec667ee 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-create-group-dialog',
+    _legacyUndefinedCheck: true,
 
     properties: {
       params: Object,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js
index 4e9da90..65bb46d 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.js
@@ -24,6 +24,7 @@
 
   Polymer({
     is: 'gr-create-pointer-dialog',
+    _legacyUndefinedCheck: true,
 
     properties: {
       detailType: String,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js
index bb2b5f2..ef7edd4 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js
+++ b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-create-repo-dialog',
+    _legacyUndefinedCheck: true,
 
     properties: {
       params: Object,
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js
index bc6a5d0..966f3c9 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js
@@ -21,6 +21,7 @@
 
   Polymer({
     is: 'gr-group-audit-log',
+    _legacyUndefinedCheck: true,
 
     properties: {
       groupId: String,
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js
index 2e36a38..76bd558 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.js
@@ -25,6 +25,7 @@
 
   Polymer({
     is: 'gr-group-members',
+    _legacyUndefinedCheck: true,
 
     properties: {
       groupId: Number,
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group.js b/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
index e2ecfba..095a0f9 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group.js
@@ -32,6 +32,7 @@
 
   Polymer({
     is: 'gr-group',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the group name changes.
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
index c45ffd5..a131f4f 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
@@ -37,6 +37,7 @@
 
   Polymer({
     is: 'gr-permission',
+    _legacyUndefinedCheck: true,
 
     properties: {
       labels: Object,
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js
index 6cbc94a..d533f98 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-plugin-list',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
index ebb0e34..713bf02 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
@@ -70,6 +70,7 @@
 
   Polymer({
     is: 'gr-repo-access',
+    _legacyUndefinedCheck: true,
 
     properties: {
       repo: {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.js b/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.js
index bcdb7f6..e0becaf 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-command/gr-repo-command.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-repo-command',
+    _legacyUndefinedCheck: true,
 
     properties: {
       title: String,
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.js b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.js
index a25055e..92b11fc 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.js
@@ -28,6 +28,7 @@
 
   Polymer({
     is: 'gr-repo-commands',
+    _legacyUndefinedCheck: true,
 
     properties: {
       params: Object,
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js
index 7dea7f4..72ee83d 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-repo-dashboards',
+    _legacyUndefinedCheck: true,
 
     properties: {
       repo: {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js
index 8c7973d..8b91977 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-detail-list/gr-repo-detail-list.js
@@ -26,6 +26,7 @@
 
   Polymer({
     is: 'gr-repo-detail-list',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js
index e0b054b..6b46cef 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-repo-list',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
index d79bf0d..840a529 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.js
@@ -53,6 +53,7 @@
 
   Polymer({
     is: 'gr-repo',
+    _legacyUndefinedCheck: true,
 
     properties: {
       params: Object,
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
index 06f703f..a45b391 100644
--- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
+++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
@@ -65,6 +65,7 @@
 
   Polymer({
     is: 'gr-rule-editor',
+    _legacyUndefinedCheck: true,
 
     properties: {
       hasRange: Boolean,
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
index 72ea3f0..ecc7532 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.js
@@ -26,6 +26,7 @@
 
   Polymer({
     is: 'gr-change-list-item',
+    _legacyUndefinedCheck: true,
 
     properties: {
       visibleChangeTableColumns: Array,
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
index d5ae7c1..d546fe3 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.js
@@ -31,6 +31,7 @@
 
   Polymer({
     is: 'gr-change-list-view',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the title of the page should change.
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
index 765b49d..2214d52 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
@@ -24,6 +24,7 @@
 
   Polymer({
     is: 'gr-change-list',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when next page key shortcut was pressed.
@@ -351,7 +352,9 @@
     },
 
     _getListItems() {
-      return Polymer.dom(this.root).querySelectorAll('gr-change-list-item');
+      // Polymer2: querySelectorAll returns NodeList instead of Array.
+      return Array.from(
+          Polymer.dom(this.root).querySelectorAll('gr-change-list-item'));
     },
 
     _sectionsChanged() {
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.js b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.js
index b5b02a7..42d7bd7 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-change-help/gr-create-change-help.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-create-change-help',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the "Create change" button is tapped.
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.js b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.js
index 0e71f1c..e4958c5 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-commands-dialog/gr-create-commands-dialog.js
@@ -25,6 +25,7 @@
 
   Polymer({
     is: 'gr-create-commands-dialog',
+    _legacyUndefinedCheck: true,
     properties: {
       branch: String,
       _createNewCommitCommand: {
diff --git a/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js b/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js
index 4d2802e..ed87e9e 100644
--- a/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js
+++ b/polygerrit-ui/app/elements/change-list/gr-create-destination-dialog/gr-create-destination-dialog.js
@@ -26,6 +26,7 @@
 
   Polymer({
     is: 'gr-create-destination-dialog',
+    _legacyUndefinedCheck: true,
     properties: {
       _repo: String,
       _branch: String,
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
index 0625bbe..d7ad0f4 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
@@ -21,6 +21,7 @@
 
   Polymer({
     is: 'gr-dashboard-view',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the title of the page should change.
diff --git a/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js b/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js
index e31f9ad..14d0cb0 100644
--- a/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js
+++ b/polygerrit-ui/app/elements/change-list/gr-embed-dashboard/gr-embed-dashboard.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-embed-dashboard',
+    _legacyUndefinedCheck: true,
     properties: {
       account: Object,
       sections: Array,
diff --git a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.js b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.js
index cd6eb77..67fbd97 100644
--- a/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.js
+++ b/polygerrit-ui/app/elements/change-list/gr-repo-header/gr-repo-header.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-repo-header',
+    _legacyUndefinedCheck: true,
     properties: {
       /** @type {?String} */
       repo: {
diff --git a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.js b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.js
index cf5fefd..dc945d8 100644
--- a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.js
+++ b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-user-header',
+    _legacyUndefinedCheck: true,
     properties: {
       /** @type {?String} */
       userId: {
diff --git a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.js b/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.js
index 715ddc0..fc0b633 100644
--- a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.js
+++ b/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-account-entry',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when an account is entered.
diff --git a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js b/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js
index 950c1e8..533ae09 100644
--- a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js
+++ b/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list.js
@@ -21,6 +21,7 @@
 
   Polymer({
     is: 'gr-account-list',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when user inputs an invalid email address.
@@ -88,7 +89,9 @@
     },
 
     get accountChips() {
-      return Polymer.dom(this.root).querySelectorAll('gr-account-chip');
+      // Polymer2: querySelectorAll returns NodeList instead of Array.
+      return Array.from(
+          Polymer.dom(this.root).querySelectorAll('gr-account-chip'));
     },
 
     get focusStart() {
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
index 3e5c4f7..ca0aa16 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
@@ -193,6 +193,7 @@
 
   Polymer({
     is: 'gr-change-actions',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the change should be reloaded.
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
index f2d49d6..8ca8146 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
@@ -61,6 +61,7 @@
 
   Polymer({
     is: 'gr-change-metadata',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the change topic is changed.
diff --git a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.js b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.js
index 8ec00dd..09efa8d 100644
--- a/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.js
+++ b/polygerrit-ui/app/elements/change/gr-change-requirements/gr-change-requirements.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-change-requirements',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /** @type {?} */
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index 472a187..e8158b8 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -235,6 +235,7 @@
         --paper-tabs-selection-bar-color: var(--link-color);
       }
       paper-tab {
+        box-sizing: border-box;
         max-width: 15rem;
         --paper-tab-ink: var(--link-color);
       }
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 123a7b5..bccd21e 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -64,6 +64,7 @@
 
   Polymer({
     is: 'gr-change-view',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the title of the page should change.
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
index cbc7e42..660dfd9 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
@@ -18,6 +18,7 @@
   'use strict';
   Polymer({
     is: 'gr-comment-list',
+    _legacyUndefinedCheck: true,
 
     behaviors: [
       Gerrit.BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.js b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.js
index 837de59..ddab319 100644
--- a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.js
+++ b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-commit-info',
+    _legacyUndefinedCheck: true,
 
     properties: {
       change: Object,
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.js
index 03509ce..a371e13 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-abandon-dialog/gr-confirm-abandon-dialog.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-confirm-abandon-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.js
index 355aa78..2e8af0e 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-conflict-dialog/gr-confirm-cherrypick-conflict-dialog.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-confirm-cherrypick-conflict-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
index 214b150..fa281ec 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.js
@@ -21,6 +21,7 @@
 
   Polymer({
     is: 'gr-confirm-cherrypick-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js
index f8d7151..093dca6 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.js
@@ -21,6 +21,7 @@
 
   Polymer({
     is: 'gr-confirm-move-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js
index 29f8b95..1c3bcfb 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-confirm-rebase-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js
index 1cae866..93e21c7 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog.js
@@ -22,6 +22,7 @@
 
   Polymer({
     is: 'gr-confirm-revert-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js
index cefa045..93d38df 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-confirm-submit-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
index 4fe6d8f..9bfe3a9 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-download-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the user presses the close button.
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
index 7b79a63..250900a 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
@@ -23,6 +23,7 @@
 
   Polymer({
     is: 'gr-file-list-header',
+    _legacyUndefinedCheck: true,
 
     /**
      * @event expand-diffs
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 460512a..9d2710b2 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -63,6 +63,7 @@
 
   Polymer({
     is: 'gr-file-list',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a draft refresh should get triggered
@@ -291,7 +292,9 @@
     },
 
     get diffs() {
-      return Polymer.dom(this.root).querySelectorAll('gr-diff-host');
+      // Polymer2: querySelectorAll returns NodeList instead of Array.
+      return Array.from(
+          Polymer.dom(this.root).querySelectorAll('gr-diff-host'));
     },
 
     openDiffPrefs() {
@@ -858,7 +861,9 @@
 
     _filesChanged() {
       Polymer.dom.flush();
-      const files = Polymer.dom(this.root).querySelectorAll('.file-row');
+      // Polymer2: querySelectorAll returns NodeList instead of Array.
+      const files = Array.from(
+          Polymer.dom(this.root).querySelectorAll('.file-row'));
       this.$.fileCursor.stops = files;
       this.$.fileCursor.setCursorAtIndex(this.selectedIndex, true);
     },
diff --git a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.js b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.js
index d2ff035..7755a60 100644
--- a/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-included-in-dialog/gr-included-in-dialog.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-included-in-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the user presses the close button.
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.html b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.html
index 0ac5019..27c5baa 100644
--- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.html
+++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.html
@@ -61,7 +61,7 @@
           background-color: var(--button-background-color, var(--table-header-background-color));
           color: var(--primary-text-color);
           padding: .2em .85em;
-          @apply(--vote-chip-styles);
+          @apply --vote-chip-styles;
         }
       }
       gr-button.iron-selected.max {
diff --git a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.js b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.js
index f472331..d9b961f 100644
--- a/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.js
+++ b/polygerrit-ui/app/elements/change/gr-label-score-row/gr-label-score-row.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-label-score-row',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when any label is changed.
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.js b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.js
index 8734da2..eaf39bc 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.js
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-label-scores',
+    _legacyUndefinedCheck: true,
     properties: {
       _labels: {
         type: Array,
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.js b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
index 8ecd6b0..837ffe0 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.js
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
@@ -22,6 +22,7 @@
 
   Polymer({
     is: 'gr-message',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when this message's reply link is tapped.
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
index 023431c..81c3322 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
@@ -27,6 +27,7 @@
 
   Polymer({
     is: 'gr-messages-list',
+    _legacyUndefinedCheck: true,
 
     properties: {
       changeNum: Number,
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
index 6c47589..8a8b6b3 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-related-changes-list',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a new section is loaded so that the change view can determine
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
index 9f9f026..8ca9128 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
@@ -58,6 +58,7 @@
 
   Polymer({
     is: 'gr-reply-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a reply is successfully sent.
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
index ab1f55e..1cae50a 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-reviewer-list',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the "Add reviewer..." button is tapped.
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js
index 69d77f6..8f15a06 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.js
@@ -25,6 +25,7 @@
 
   Polymer({
     is: 'gr-thread-list',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /** @type {?} */
diff --git a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.js b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.js
index 02d00cf..df96be2 100644
--- a/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-upload-help-dialog/gr-upload-help-dialog.js
@@ -29,6 +29,7 @@
 
   Polymer({
     is: 'gr-upload-help-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the user presses the close button.
diff --git a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js
index 72ec7fa..af4510d 100644
--- a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js
+++ b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.js
@@ -21,6 +21,7 @@
 
   Polymer({
     is: 'gr-account-dropdown',
+    _legacyUndefinedCheck: true,
 
     properties: {
       account: Object,
diff --git a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js
index 8d3b58e..5679408 100644
--- a/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js
+++ b/polygerrit-ui/app/elements/core/gr-error-dialog/gr-error-dialog.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-error-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the dismiss button is pressed.
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
index 16a0fd0..38dfecb 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager.js
@@ -27,6 +27,7 @@
 
   Polymer({
     is: 'gr-error-manager',
+    _legacyUndefinedCheck: true,
 
     behaviors: [
       Gerrit.BaseUrlBehavior,
diff --git a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js
index 89d1091..e8c6479 100644
--- a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js
+++ b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-key-binding-display',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /** @type {Array<string>} */
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
index 5b29972..f206db1 100644
--- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
+++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.js
@@ -21,6 +21,7 @@
 
   Polymer({
     is: 'gr-keyboard-shortcuts-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the user presses the close button.
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
index a77c66d..1f2c720 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
@@ -71,6 +71,7 @@
 
   Polymer({
     is: 'gr-main-header',
+    _legacyUndefinedCheck: true,
 
     hostAttributes: {
       role: 'banner',
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
index 59af36d..2cee55c 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting.js
@@ -144,6 +144,7 @@
   // eslint-disable-next-line prefer-const
   let GrReporting = Polymer({
     is: 'gr-reporting',
+    _legacyUndefinedCheck: true,
 
     properties: {
       category: String,
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index af4c345..e6ad03e 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -204,6 +204,7 @@
 
   Polymer({
     is: 'gr-router',
+    _legacyUndefinedCheck: true,
 
     properties: {
       _app: {
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
index 17f883f..e37fb2e 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
@@ -106,6 +106,7 @@
 
   Polymer({
     is: 'gr-search-bar',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a search is committed
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js
index a921308..fed02d6 100644
--- a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js
@@ -23,6 +23,7 @@
 
   Polymer({
     is: 'gr-smart-search',
+    _legacyUndefinedCheck: true,
 
     properties: {
       searchQuery: String,
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock.js b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock.js
index b7994e6..7bf71f5 100644
--- a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock.js
+++ b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api-mock.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'comment-api-mock',
+    _legacyUndefinedCheck: true,
 
     properties: {
       _changeComments: Object,
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js
index 4b64f7b..b5ff9a3 100644
--- a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js
+++ b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.js
@@ -482,6 +482,7 @@
 
   Polymer({
     is: 'gr-comment-api',
+    _legacyUndefinedCheck: true,
 
     properties: {
       _changeComments: Object,
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
index 9646b06..b5f21b6 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.html
@@ -15,7 +15,6 @@
 limitations under the License.
 -->
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
 <link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
 <link rel="import" href="../gr-diff-processor/gr-diff-processor.html">
 <link rel="import" href="../gr-ranged-comment-layer/gr-ranged-comment-layer.html">
@@ -35,7 +34,6 @@
     <gr-diff-processor
         id="processor"
         groups="{{_groups}}"></gr-diff-processor>
-    <gr-reporting id="reporting"></gr-reporting>
     <gr-js-api-interface id="jsAPI"></gr-js-api-interface>
   </template>
   <script src="../../../scripts/util.js"></script>
@@ -56,12 +54,6 @@
         UNIFIED: 'UNIFIED_DIFF',
       };
 
-      const TimingLabel = {
-        TOTAL: 'Diff Total Render',
-        CONTENT: 'Diff Content Render',
-        SYNTAX: 'Diff Syntax Render',
-      };
-
       // If any line of the diff is more than the character limit, then disable
       // syntax highlighting for the entire file.
       const SYNTAX_MAX_LINE_LENGTH = 500;
@@ -73,6 +65,7 @@
 
       Polymer({
         is: 'gr-diff-builder',
+        _legacyUndefinedCheck: true,
 
         /**
          * Fired when the diff begins rendering.
@@ -81,12 +74,18 @@
          */
 
         /**
-         * Fired when the diff finishes rendering text content, but not
-         * necessarily syntax highlights.
+         * Fired when the diff finishes rendering text content and starts
+         * syntax highlighting.
          *
          * @event render-content
          */
 
+        /**
+         * Fired when the diff finishes syntax highlighting.
+         *
+         * @event render-syntax
+         */
+
         properties: {
           diff: Object,
           diffPath: String,
@@ -148,11 +147,8 @@
           this._clearDiffContent();
           this._builder.addColumns(this.diffElement, prefs.font_size);
 
-          const reporting = this.$.reporting;
           const isBinary = !!(this.isImageDiff || this.diff.binary);
 
-          reporting.time(TimingLabel.TOTAL);
-          reporting.time(TimingLabel.CONTENT);
           this.dispatchEvent(new CustomEvent('render-start', {bubbles: true}));
           this._cancelableRenderPromise = util.makeCancelable(
               this.$.processor.process(this.diff.content, isBinary)
@@ -167,12 +163,11 @@
                       this.$.syntaxLayer.enabled = false;
                     }
 
-                    reporting.timeEnd(TimingLabel.CONTENT);
-                    reporting.time(TimingLabel.SYNTAX);
-                    return this.$.syntaxLayer.process().then(() => {
-                      reporting.timeEnd(TimingLabel.SYNTAX);
-                      reporting.timeEnd(TimingLabel.TOTAL);
-                    });
+                    return this.$.syntaxLayer.process();
+                  })
+                  .then(() => {
+                    this.dispatchEvent(
+                        new CustomEvent('render-syntax', {bubbles: true}));
                   }));
           return this._cancelableRenderPromise
               .finally(() => { this._cancelableRenderPromise = null; })
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
index 67d8c19..481db57 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
@@ -783,10 +783,6 @@
             ],
           },
         ];
-        stub('gr-reporting', {
-          time: sandbox.stub(),
-          timeEnd: sandbox.stub(),
-        });
         element = fixture('basic');
         outputEl = element.queryEffectiveChildren('#diffTable');
         keyLocations = {left: {}, right: {}};
@@ -806,18 +802,6 @@
         element.render(keyLocations, prefs).then(done);
       });
 
-      test('reporting', done => {
-        const timeStub = element.$.reporting.time;
-        const timeEndStub = element.$.reporting.timeEnd;
-        assert.isTrue(timeStub.calledWithExactly('Diff Total Render'));
-        assert.isTrue(timeStub.calledWithExactly('Diff Content Render'));
-        assert.isTrue(timeStub.calledWithExactly('Diff Syntax Render'));
-        assert.isTrue(timeEndStub.calledWithExactly('Diff Total Render'));
-        assert.isTrue(timeEndStub.calledWithExactly('Diff Content Render'));
-        assert.isTrue(timeEndStub.calledWithExactly('Diff Syntax Render'));
-        done();
-      });
-
       test('renderSection', () => {
         let section = outputEl.querySelector('stub:nth-of-type(2)');
         const prevInnerHTML = section.innerHTML;
@@ -857,6 +841,7 @@
               .map(c => { return c.args[0].type; });
           assert.include(firedEventTypes, 'render-start');
           assert.include(firedEventTypes, 'render-content');
+          assert.include(firedEventTypes, 'render-syntax');
           done();
         });
       });
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
index 860d900..485b0bb 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
@@ -37,6 +37,7 @@
 
   Polymer({
     is: 'gr-diff-cursor',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js
index 868dc6c..ad9e99b 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-diff-highlight',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /** @type {!Array<!Gerrit.HoveredRange>} */
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
index fd460cc..94f6ce9 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
@@ -28,6 +28,13 @@
     UNIFIED: 'UNIFIED_DIFF',
   };
 
+  /** @enum {string} */
+  const TimingLabel = {
+    TOTAL: 'Diff Total Render',
+    CONTENT: 'Diff Content Render',
+    SYNTAX: 'Diff Syntax Render',
+  };
+
   const WHITESPACE_IGNORE_NONE = 'IGNORE_NONE';
 
   /**
@@ -59,6 +66,7 @@
    */
   Polymer({
     is: 'gr-diff-host',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the user selects a line.
@@ -201,6 +209,12 @@
       'comment-discard': '_handleCommentDiscard',
       'comment-update': '_handleCommentUpdate',
       'comment-save': '_handleCommentSave',
+
+      'render-start': '_handleRenderStart',
+      'render-content': '_handleRenderContent',
+      'render-syntax': '_handleRenderSyntax',
+
+      'normalize-range': '_handleNormalizeRange',
     },
 
     observers: [
@@ -327,7 +341,9 @@
      * @return {!Array<!HTMLElement>}
      */
     getThreadEls() {
-      return Polymer.dom(this.$.diff).querySelectorAll('.comment-thread');
+      // Polymer2: querySelectorAll returns NodeList instead of Array.
+      return Array.from(
+          Polymer.dom(this.$.diff).querySelectorAll('.comment-thread'));
     },
 
     /** @param {HTMLElement} el */
@@ -825,5 +841,26 @@
       return this.comments[side].findIndex(
           item => item.__draftID === comment.__draftID);
     },
+
+    _handleRenderStart() {
+      this.$.reporting.time(TimingLabel.TOTAL);
+      this.$.reporting.time(TimingLabel.CONTENT);
+    },
+
+    _handleRenderContent() {
+      this.$.reporting.timeEnd(TimingLabel.CONTENT);
+      this.$.reporting.time(TimingLabel.SYNTAX);
+    },
+
+    _handleRenderSyntax() {
+      this.$.reporting.timeEnd(TimingLabel.SYNTAX);
+      this.$.reporting.timeEnd(TimingLabel.TOTAL);
+    },
+
+    _handleNormalizeRange(event) {
+      this.$.reporting.reportInteraction('normalize-range',
+          `Modified invalid comment range on l. ${event.detail.lineNum}` +
+          ` of the ${event.detail.side} side`);
+    },
   });
 })();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
index ab9daec..7466ade 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
@@ -45,6 +45,11 @@
       stub('gr-rest-api-interface', {
         async getLoggedIn() { return getLoggedIn; },
       });
+      stub('gr-reporting', {
+        time: sandbox.stub(),
+        timeEnd: sandbox.stub(),
+      });
+
       element = fixture('basic');
     });
 
@@ -261,6 +266,38 @@
       assert.equal(attachedThreads[0].rootId, 42);
     });
 
+    suite('render reporting', () => {
+      test('starts total and content timer on render-start', done => {
+        element.dispatchEvent(
+            new CustomEvent('render-start', {bubbles: true}));
+        assert.isTrue(element.$.reporting.time.calledWithExactly(
+            'Diff Total Render'));
+        assert.isTrue(element.$.reporting.time.calledWithExactly(
+            'Diff Content Render'));
+        done();
+      });
+
+      test('ends content and starts syntax timer on render-content', done => {
+        element.dispatchEvent(
+            new CustomEvent('render-content', {bubbles: true}));
+        assert.isTrue(element.$.reporting.time.calledWithExactly(
+            'Diff Syntax Render'));
+        assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
+            'Diff Content Render'));
+        done();
+      });
+
+      test('ends total and syntax timer on render-syntax', done => {
+        element.dispatchEvent(
+            new CustomEvent('render-syntax', {bubbles: true}));
+        assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
+            'Diff Total Render'));
+        assert.isTrue(element.$.reporting.timeEnd.calledWithExactly(
+            'Diff Syntax Render'));
+        done();
+      });
+    });
+
     test('reload() cancels before network resolves', () => {
       const cancelStub = sandbox.stub(element.$.diff, 'cancel');
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.js b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.js
index 88dd91a..e2d6a28 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-mode-selector/gr-diff-mode-selector.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-diff-mode-selector',
+    _legacyUndefinedCheck: true,
 
     properties: {
       mode: {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js
index b50ef69..bcc3c2c 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-diff-preferences-dialog',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /** @type {?} */
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
index a9268a7..a1af8e1 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor.js
@@ -45,8 +45,22 @@
    */
   const MAX_GROUP_SIZE = 120;
 
+  /**
+   * Converts the API's `DiffContent`s  to `GrDiffGroup`s for rendering.
+   *
+   * This includes a number of tasks:
+   *  - adding a group for the "File" pseudo line that file-level comments can
+   *    be attached to
+   *  - replacing unchanged parts of the diff that are outside the user's
+   *    context setting and do not have comments with a group representing the
+   *    "expand context" widget. This may require splitting a `DiffContent` so
+   *    that the part that is within the context or has comments is shown, while
+   *    the rest is not.
+   *  - splitting large `DiffContent`s to allow more granular async rendering
+   */
   Polymer({
     is: 'gr-diff-processor',
+    _legacyUndefinedCheck: true,
 
     properties: {
 
@@ -137,7 +151,8 @@
               sectionIndex: 0,
             };
 
-            content = this._splitCommonGroupsWithComments(content);
+            content = this._splitLargeChunks(content);
+            content = this._splitUnchangedChunksWithComments(content);
 
             let currentBatch = 0;
             const nextStep = () => {
@@ -370,60 +385,78 @@
       return new GrDiffGroup(GrDiffGroup.Type.BOTH, [line]);
     },
 
+
+    /**
+     * Split chunks into smaller chunks of the same kind.
+     *
+     * This is done to prevent doing too much work on the main thread in one
+     * uninterrupted rendering step, which would make the browser unresponsive.
+     *
+     * Note that in the case of unmodified chunks, we only split chunks if the
+     * context is set to file (because otherwise they are split up further down
+     * the processing into the visible and hidden context), and only split it
+     * into 2 chunks, one max sized one and the rest (for reasons that are
+     * unclear to me).
+     *
+     * @param {!Array<!Object>} chunks Chunks as returned from the server
+     * @return {!Array<!Object>} Finer grained chunks.
+     */
+    _splitLargeChunks(chunks) {
+      const newChunks = [];
+
+      for (const chunk of chunks) {
+        if (!chunk.ab) {
+          for (const group of this._breakdownGroup(chunk)) {
+            newChunks.push(group);
+          }
+          continue;
+        }
+
+        // If the context is set to "whole file", then break down the shared
+        // chunks so they can be rendered incrementally. Note: this is not
+        // enabled for any other context preference because manipulating the
+        // chunks in this way violates assumptions by the context grouper logic.
+        if (this.context === -1 && chunk.ab.length > MAX_GROUP_SIZE * 2) {
+          // Split large shared groups in two, where the first is the maximum
+          // group size.
+          newChunks.push({ab: chunk.ab.slice(0, MAX_GROUP_SIZE)});
+          newChunks.push({ab: chunk.ab.slice(MAX_GROUP_SIZE)});
+        } else {
+          newChunks.push(chunk);
+        }
+      }
+      return newChunks;
+    },
+
     /**
      * In order to show comments out of the bounds of the selected context,
      * treat them as separate chunks within the model so that the content (and
      * context surrounding it) renders correctly.
-     * @param {?} content The diff content object. (has to be iterable)
-     * @return {!Object} A new diff content object with regions split up.
+     * @param {!Array<!Object>} chunks DiffContents as returned from server.
+     * @return {!Array<!Object>} Finer grained DiffContents.
      */
-    _splitCommonGroupsWithComments(content) {
+    _splitUnchangedChunksWithComments(chunks) {
       const result = [];
       let leftLineNum = 0;
       let rightLineNum = 0;
 
-      // If the context is set to "whole file", then break down the shared
-      // chunks so they can be rendered incrementally. Note: this is not enabled
-      // for any other context preference because manipulating the chunks in
-      // this way violates assumptions by the context grouper logic.
-      if (this.context === -1) {
-        const newContent = [];
-        for (const group of content) {
-          if (group.ab && group.ab.length > MAX_GROUP_SIZE * 2) {
-            // Split large shared groups in two, where the first is the maximum
-            // group size.
-            newContent.push({ab: group.ab.slice(0, MAX_GROUP_SIZE)});
-            newContent.push({ab: group.ab.slice(MAX_GROUP_SIZE)});
-          } else {
-            newContent.push(group);
+      for (const chunk of chunks) {
+        // If it isn't a common chunk, append it as-is and update line numbers.
+        if (!chunk.ab) {
+          if (chunk.a) {
+            leftLineNum += chunk.a.length;
           }
-        }
-        content = newContent;
-      }
-
-      // For each section in the diff.
-      for (let i = 0; i < content.length; i++) {
-        // If it isn't a common group, append it as-is and update line numbers.
-        if (!content[i].ab) {
-          if (content[i].a) {
-            leftLineNum += content[i].a.length;
+          if (chunk.b) {
+            rightLineNum += chunk.b.length;
           }
-          if (content[i].b) {
-            rightLineNum += content[i].b.length;
-          }
-
-          for (const group of this._breakdownGroup(content[i])) {
-            result.push(group);
-          }
-
+          result.push(chunk);
           continue;
         }
 
-        const chunk = content[i].ab;
         let currentChunk = {ab: []};
 
         // For each line in the common group.
-        for (const subChunk of chunk) {
+        for (const line of chunk.ab) {
           leftLineNum++;
           rightLineNum++;
 
@@ -439,10 +472,10 @@
             }
 
             // Add the non-collapse line as its own chunk.
-            result.push({ab: [subChunk]});
+            result.push({ab: [line]});
           } else {
             // Append the current line to the current chunk.
-            currentChunk.ab.push(subChunk);
+            currentChunk.ab.push(line);
           }
         }
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
index 7ccd9f8..186a49e 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
@@ -280,7 +280,6 @@
           left: {1: true},
           right: {10: true},
         };
-        const lineNums = {left: 0, right: 0};
 
         const content = [
           {
@@ -304,7 +303,7 @@
           },
         ];
         const result =
-            element._splitCommonGroupsWithComments(content, lineNums);
+            element._splitUnchangedChunksWithComments(content);
         assert.deepEqual(result, [
           {
             ab: ['Copyright (C) 2015 The Android Open Source Project'],
@@ -337,28 +336,25 @@
         ]);
       });
 
-      test('breaks-down shared chunks w/ whole-file', () => {
+      test('breaks down shared chunks w/ whole-file', () => {
         const size = 120 * 2 + 5;
-        const lineNums = {left: 0, right: 0};
         const content = [{
           ab: _.times(size, () => { return `${Math.random()}`; }),
         }];
         element.context = -1;
-        const result =
-            element._splitCommonGroupsWithComments(content, lineNums);
+        const result = element._splitLargeChunks(content);
         assert.equal(result.length, 2);
         assert.deepEqual(result[0].ab, content[0].ab.slice(0, 120));
         assert.deepEqual(result[1].ab, content[0].ab.slice(120));
       });
 
       test('does not break-down shared chunks w/ context', () => {
-        const lineNums = {left: 0, right: 0};
         const content = [{
           ab: _.times(75, () => { return `${Math.random()}`; }),
         }];
         element.context = 4;
         const result =
-            element._splitCommonGroupsWithComments(content, lineNums);
+            element._splitUnchangedChunksWithComments(content);
         assert.deepEqual(result, content);
       });
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
index 6a9d88f..072a0d7 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
@@ -32,6 +32,7 @@
 
   Polymer({
     is: 'gr-diff-selection',
+    _legacyUndefinedCheck: true,
 
     properties: {
       diff: Object,
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index c8b3c4b..d37c97d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -35,6 +35,7 @@
 
   Polymer({
     is: 'gr-diff-view',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the title of the page should change.
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.js
index 88fcd0e..dd69724 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.js
@@ -20,11 +20,20 @@
   // Prevent redefinition.
   if (window.GrDiffGroup) { return; }
 
+  /**
+   * A chunk of the diff that should be rendered together.
+   */
   function GrDiffGroup(type, opt_lines) {
     this.type = type;
+
+    /** @type{!Array<!GrDiffLine>} */
     this.lines = [];
+    /** @type{!Array<!GrDiffLine>} */
     this.adds = [];
+    /** @type{!Array<!GrDiffLine>} */
     this.removes = [];
+
+    /** @type{boolean|undefined} */
     this.dueToRebase = undefined;
 
     this.lineRange = {
@@ -40,8 +49,13 @@
   GrDiffGroup.prototype.element = null;
 
   GrDiffGroup.Type = {
+    /** Unchanged context. */
     BOTH: 'both',
+
+    /** A widget used to show more context. */
     CONTEXT_CONTROL: 'contextControl',
+
+    /** Added, removed or modified chunk. */
     DELTA: 'delta',
   };
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
index 4ccdf96..61e8603 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -184,7 +184,12 @@
         /* >> character */
         content: '\00BB';
       }
-      .trailing-whitespace {
+      /* Is defined after other background-colors, such that this
+         rule wins in case of same specificity. */
+      .trailing-whitespace,
+      .content .trailing-whitespace,
+      .trailing-whitespace .intraline,
+      .content .trailing-whitespace .intraline {
         border-radius: .4em;
         background-color: var(--diff-trailing-whitespace-indicator);
       }
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
index 2146d9d..24f167d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -106,6 +106,7 @@
 
   Polymer({
     is: 'gr-diff',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the user selects a line.
@@ -406,7 +407,9 @@
         return [];
       }
 
-      return Polymer.dom(this.root).querySelectorAll('.diff-row');
+      // Polymer2: querySelectorAll returns NodeList instead of Array.
+      return Array.from(
+          Polymer.dom(this.root).querySelectorAll('.diff-row'));
     },
 
     /** @return {boolean} */
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
index cf8118f..ea8aa47 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.js
@@ -31,6 +31,7 @@
 
   Polymer({
     is: 'gr-patch-range-select',
+    _legacyUndefinedCheck: true,
 
     properties: {
       availablePatches: Array,
diff --git a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.html b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.html
index 71b5bc3..c9e9f50 100644
--- a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.html
+++ b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.html
@@ -16,10 +16,8 @@
 -->
 
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
 <dom-module id="gr-ranged-comment-layer">
   <template>
-    <gr-reporting id="reporting" category="comments"></gr-reporting>
   </template>
   <script src="../gr-diff-highlight/gr-annotation.js"></script>
   <script src="gr-ranged-comment-layer.js"></script>
diff --git a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js
index 426c871..e28142b 100644
--- a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js
+++ b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer.js
@@ -22,13 +22,21 @@
   const RANGE_HIGHLIGHT = 'range';
   const HOVER_HIGHLIGHT = 'rangeHighlight';
 
-  const NORMALIZE_RANGE_EVENT = 'normalize-range';
-
   /** @typedef {{side: string, range: Gerrit.Range, hovering: boolean}} */
   Gerrit.HoveredRange;
 
   Polymer({
     is: 'gr-ranged-comment-layer',
+    _legacyUndefinedCheck: true,
+
+    /**
+     * Fired when the range in a range comment was malformed and had to be
+     * normalized.
+     *
+     * It's `detail` has a `lineNum` and `side` parameter.
+     *
+     * @event normalize-range
+     */
 
     properties: {
       /** @type {!Array<!Gerrit.HoveredRange>} */
@@ -178,9 +186,10 @@
             // @see Issue 5744
             if (range.start >= range.end && range.start < line.text.length) {
               range.end = line.text.length;
-              this.$.reporting.reportInteraction(NORMALIZE_RANGE_EVENT,
-                  'Modified invalid comment range on l.' + lineNum +
-                  ' of the ' + side + ' side');
+              this.dispatchEvent(new CustomEvent('normalize-range', {
+                bubbles: true,
+                detail: {lineNum, side},
+              }));
             }
 
             return range;
diff --git a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.js b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.js
index abf8e73..26bf738 100644
--- a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.js
+++ b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-selection-action-box',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the comment creation action was taken (hotkey, click).
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
index 32eb969..32724bd 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer.js
@@ -129,6 +129,7 @@
 
   Polymer({
     is: 'gr-syntax-layer',
+    _legacyUndefinedCheck: true,
 
     properties: {
       diff: {
diff --git a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.js b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.js
index f850b9d..995326f 100644
--- a/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.js
+++ b/polygerrit-ui/app/elements/documentation/gr-documentation-search/gr-documentation-search.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-documentation-search',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.js b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.js
index 168ded8..01cd9df 100644
--- a/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.js
+++ b/polygerrit-ui/app/elements/edit/gr-default-editor/gr-default-editor.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-default-editor',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the content of the editor changes.
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
index 8740b0d..2658bd2 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-edit-controls',
+    _legacyUndefinedCheck: true,
     properties: {
       change: Object,
       patchNum: String,
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js
index 82010f5..9407f18 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-file-controls/gr-edit-file-controls.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-edit-file-controls',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when an action in the overflow menu is tapped.
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js
index bf7fc99..8e108bb 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.js
@@ -26,6 +26,7 @@
 
   Polymer({
     is: 'gr-editor-view',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the title of the page should change.
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index 0301a13..e537ac4 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -15,16 +15,18 @@
 limitations under the License.
 -->
 <script>
-  // This must be set prior to loading Polymer for the first time.
-  if (localStorage.getItem('USE_SHADOW_DOM') === 'true') {
-    window.Polymer = {
-      dom: 'shadow',
-      passiveTouchGestures: true,
-    };
-  } else if (!window.Polymer) {
-    window.Polymer = {
-      passiveTouchGestures: true,
-    };
+  if (!window.POLYMER2) {
+    // This must be set prior to loading Polymer for the first time.
+    if (localStorage.getItem('USE_SHADOW_DOM') === 'true') {
+      window.Polymer = {
+        dom: 'shadow',
+        passiveTouchGestures: true,
+      };
+    } else if (!window.Polymer) {
+      window.Polymer = {
+        passiveTouchGestures: true,
+      };
+    }
   }
   // Needed for JSCompiler to understand it's global.
   // eslint-disable-next-line no-unused-vars, prefer-const
@@ -34,6 +36,7 @@
 
 <link rel="import" href="../bower_components/polymer/polymer.html">
 <link rel="import" href="../bower_components/polymer-resin/standalone/polymer-resin.html">
+<link rel="import" href="../bower_components/polymer/lib/legacy/legacy-data-mixin.html">
 <link rel="import" href="../behaviors/safe-types-behavior/safe-types-behavior.html">
 <script>
   security.polymer_resin.install({
@@ -147,6 +150,7 @@
         color: var(--error-text-color);
       }
     </style>
+    <gr-endpoint-decorator name="banner"></gr-endpoint-decorator>
     <gr-fixed-panel id="header">
       <gr-main-header
           id="mainHeader"
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index f2f06d1..a2a495b 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -20,12 +20,14 @@
   // Eagerly render Polymer components when backgrounded. (Skips
   // requestAnimationFrame.)
   // @see https://github.com/Polymer/polymer/issues/3851
-  // TODO: Reassess after Polymer 2.0 upgrade.
   // @see Issue 4699
-  Polymer.RenderStatus._makeReady();
+  if (!window.POLYMER2) {
+    Polymer.RenderStatus._makeReady();
+  }
 
   Polymer({
     is: 'gr-app',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the URL location changes.
diff --git a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.html b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.html
index cd59f7d..86238cf 100644
--- a/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-attribute-helper/gr-attribute-helper_test.html
@@ -30,6 +30,7 @@
   <script>
     Polymer({
       is: 'some-element',
+      _legacyUndefinedCheck: true,
       properties: {
         fooBar: {
           type: Object,
diff --git a/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.js b/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.js
index 230be0e..e0fa61e 100644
--- a/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.js
+++ b/polygerrit-ui/app/elements/plugins/gr-dom-hooks/gr-dom-hooks.js
@@ -54,6 +54,7 @@
   GrDomHook.prototype._createPlaceholder = function(hookName) {
     Polymer({
       is: hookName,
+      _legacyUndefinedCheck: true,
       properties: {
         plugin: Object,
         content: Object,
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js
index fe82f2b..35e4292 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator.js
@@ -21,6 +21,7 @@
 
   Polymer({
     is: 'gr-endpoint-decorator',
+    _legacyUndefinedCheck: true,
 
     properties: {
       name: String,
@@ -71,7 +72,9 @@
     },
 
     _getEndpointParams() {
-      return Polymer.dom(this).querySelectorAll('gr-endpoint-param');
+      // Polymer2: querySelectorAll returns NodeList instead of Array.
+      return Array.from(
+          Polymer.dom(this).querySelectorAll('gr-endpoint-param'));
     },
 
     /**
diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js b/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js
index cbc3d6a..e21fc72 100644
--- a/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js
+++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-param/gr-endpoint-param.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-endpoint-param',
+    _legacyUndefinedCheck: true,
     properties: {
       name: String,
       value: {
diff --git a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
index 709c042..47274f6 100644
--- a/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-event-helper/gr-event-helper_test.html
@@ -30,6 +30,7 @@
   <script>
     Polymer({
       is: 'some-element',
+      _legacyUndefinedCheck: true,
       properties: {
         fooBar: {
           type: Object,
diff --git a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js
index 0e8bb45..7924e27 100644
--- a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js
+++ b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-external-style',
+    _legacyUndefinedCheck: true,
 
     properties: {
       name: String,
diff --git a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js
index 111cdc6..4b5d4f0 100644
--- a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js
+++ b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-plugin-host',
+    _legacyUndefinedCheck: true,
 
     properties: {
       config: {
diff --git a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js
index dd37f84..3ef93e4 100644
--- a/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js
+++ b/polygerrit-ui/app/elements/plugins/gr-popup-interface/gr-plugin-popup.js
@@ -18,6 +18,7 @@
   'use strict';
   Polymer({
     is: 'gr-plugin-popup',
+    _legacyUndefinedCheck: true,
     get opened() {
       return this.$.overlay.opened;
     },
diff --git a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.html b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.html
index e47ba15..c9486ae 100644
--- a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.html
+++ b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-plugin-repo-command.html
@@ -25,6 +25,7 @@
   <script>
     Polymer({
       is: 'gr-plugin-repo-command',
+      _legacyUndefinedCheck: true,
       properties: {
         title: String,
         repoName: String,
diff --git a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html
index e6e8fa5..496d0e7 100644
--- a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html
+++ b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-custom-plugin-header.html
@@ -37,6 +37,7 @@
   <script>
     Polymer({
       is: 'gr-custom-plugin-header',
+      _legacyUndefinedCheck: true,
       properties: {
         logoUrl: String,
         title: String,
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js
index fcc99aa..feeb7df 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-account-info',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when account details are changed.
diff --git a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.js b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.js
index 41595a98..fe36a86 100644
--- a/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.js
+++ b/polygerrit-ui/app/elements/settings/gr-agreements-list/gr-agreements-list.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-agreements-list',
+    _legacyUndefinedCheck: true,
 
     properties: {
       _agreements: Array,
diff --git a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.js b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.js
index 7d109633..3909c99 100644
--- a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-change-table-editor',
+    _legacyUndefinedCheck: true,
 
     properties: {
       displayedColumns: {
@@ -41,8 +42,9 @@
      * @return {!Array<string>}
      */
     _getDisplayedColumns() {
-      return Polymer.dom(this.root)
-          .querySelectorAll('.checkboxContainer input:not([name=number])')
+      // Polymer2: querySelectorAll returns NodeList instead of Array.
+      return Array.from(Polymer.dom(this.root)
+          .querySelectorAll('.checkboxContainer input:not([name=number])'))
           .filter(checkbox => checkbox.checked)
           .map(checkbox => checkbox.name);
     },
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js
index c771332..a59b886 100644
--- a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js
+++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-cla-view',
+    _legacyUndefinedCheck: true,
 
     properties: {
       _groups: Object,
diff --git a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.js b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.js
index 86350f9..37bce08 100644
--- a/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.js
+++ b/polygerrit-ui/app/elements/settings/gr-edit-preferences/gr-edit-preferences.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-edit-preferences',
+    _legacyUndefinedCheck: true,
 
     properties: {
       hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js
index d08cc90..71d75cc 100644
--- a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-email-editor',
+    _legacyUndefinedCheck: true,
 
     properties: {
       hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js
index 78025d1..7348067 100644
--- a/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-gpg-editor/gr-gpg-editor.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-gpg-editor',
+    _legacyUndefinedCheck: true,
 
     properties: {
       hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.js b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.js
index 0f43563..4de24aa 100644
--- a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.js
+++ b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-group-list',
+    _legacyUndefinedCheck: true,
 
     properties: {
       _groups: Array,
diff --git a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.js b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.js
index b3d1396..cda6da7 100644
--- a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.js
+++ b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-http-password',
+    _legacyUndefinedCheck: true,
 
     properties: {
       _username: String,
diff --git a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js
index da6ab28..0d053f7 100644
--- a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js
+++ b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-identities',
+    _legacyUndefinedCheck: true,
 
     properties: {
       _identities: Object,
diff --git a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.js b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.js
index aa83bf7..8587338 100644
--- a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-menu-editor',
+    _legacyUndefinedCheck: true,
 
     properties: {
       menuItems: Array,
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
index c6cd578..6b4ee18 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-registration-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when account details are changed.
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.html
index b61c7e0..30a3801 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.html
@@ -18,13 +18,13 @@
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
 
 <dom-module id="gr-settings-item">
-  <style>
-    :host {
-      display: block;
-      margin-bottom: 2em;
-    }
-  </style>
   <template>
+    <style>
+      :host {
+        display: block;
+        margin-bottom: 2em;
+      }
+    </style>
     <h2 id="[[anchor]]">[[title]]</h2>
     <slot></slot>
   </template>
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.js
index 0ee1b28..dc1aa93 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-item.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-settings-item',
+    _legacyUndefinedCheck: true,
     properties: {
       anchor: String,
       title: String,
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.html
index 3b47190..f64d898 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.html
@@ -19,9 +19,9 @@
 <link rel="import" href="../../../styles/gr-page-nav-styles.html">
 
 <dom-module id="gr-settings-menu-item">
-  <style include="shared-styles"></style>
-  <style include="gr-page-nav-styles"></style>
   <template>
+    <style include="shared-styles"></style>
+    <style include="gr-page-nav-styles"></style>
     <div class="navStyles">
       <li><a href$="[[href]]">[[title]]</a></li>
     </div>
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.js
index 38147cd..2a56b09 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-menu-item.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-settings-menu-item',
+    _legacyUndefinedCheck: true,
     properties: {
       href: String,
       title: String,
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
index f523034..1f76a68 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
@@ -47,6 +47,7 @@
 
   Polymer({
     is: 'gr-settings-view',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the title of the page should change.
diff --git a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
index 874173a..4c423e8 100644
--- a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-ssh-editor',
+    _legacyUndefinedCheck: true,
 
     properties: {
       hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.js b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.js
index ebf61db..151eb8b 100644
--- a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.js
@@ -27,6 +27,7 @@
 
   Polymer({
     is: 'gr-watched-projects-editor',
+    _legacyUndefinedCheck: true,
 
     properties: {
       hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js
index 880cbc0..827e33b 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-chip/gr-account-chip.js
@@ -20,6 +20,7 @@
 
   Polymer({
     is: 'gr-account-chip',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired to indicate a key was pressed while this chip was focused.
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
index 5b1b975..7983fad 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-account-label',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
index faaf9c3..03967f1 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
+++ b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-account-link',
+    _legacyUndefinedCheck: true,
 
     properties: {
       additionalText: String,
diff --git a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
index e7c8b2c..ec7b6eb 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-alert',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the action button is pressed.
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
index 2e55010..1af629d2 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-autocomplete-dropdown',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the dropdown is closed.
@@ -162,7 +163,9 @@
     _resetCursorStops() {
       if (this.suggestions.length > 0) {
         Polymer.dom.flush();
-        this._suggestionEls = this.$.suggestions.querySelectorAll('li');
+        // Polymer2: querySelectorAll returns NodeList instead of Array.
+        this._suggestionEls = Array.from(
+            this.$.suggestions.querySelectorAll('li'));
       } else {
         this._suggestionEls = [];
       }
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
index 4b447d1..a878174 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
@@ -29,7 +29,7 @@
         display: none;
       }
       .searchIcon.showSearchIcon {
-        display: initial;
+        display: inline-block;
       }
       iron-icon {
         margin: 0 .25em;
@@ -68,7 +68,6 @@
         no-label-float
         id="input"
         class$="[[_computeClass(borderless)]]"
-        is="iron-input"
         disabled$="[[disabled]]"
         value="{{text}}"
         placeholder="[[placeholder]]"
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
index 4efa7ad..76e14a7 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
@@ -22,6 +22,7 @@
 
   Polymer({
     is: 'gr-autocomplete',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a value is chosen.
@@ -210,7 +211,8 @@
     },
 
     get _inputElement() {
-      return this.$.input;
+      // Polymer2: this.$ can be undefined when this is first evaluated.
+      return this.$ && this.$.input;
     },
 
     /**
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
index bf56382..2435e58 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-avatar',
+    _legacyUndefinedCheck: true,
 
     properties: {
       account: {
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
index fe18180..cdf617f 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.html
@@ -94,7 +94,9 @@
       }
 
       /* Styles for the optional down arrow */
-      :host:not([down-arrow]) .downArrow {display: none; }
+      :host(:not([down-arrow])) .downArrow {
+        display: none;
+      }
       :host([down-arrow]) .downArrow {
         border-top: .36em solid #ccc;
         border-left: .36em solid transparent;
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button.js b/polygerrit-ui/app/elements/shared/gr-button/gr-button.js
index ca6705e..5988cde 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button.js
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-button',
+    _legacyUndefinedCheck: true,
 
     properties: {
       tooltip: String,
diff --git a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.js b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.js
index 3c46d1b..44f8c00 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.js
+++ b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-change-star',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when star state is toggled.
diff --git a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.html b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.html
index e256bf3..99ddff1 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.html
+++ b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.html
@@ -68,7 +68,7 @@
         background-color: transparent;
         padding: .1em;
       }
-      :host:not([flat]) .chip {
+      :host(:not([flat])) .chip {
         color: white;
       }
     </style>
diff --git a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.js b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.js
index 3afbe54..70c5d72 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.js
+++ b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.js
@@ -34,6 +34,7 @@
 
   Polymer({
     is: 'gr-change-status',
+    _legacyUndefinedCheck: true,
 
     properties: {
       flat: {
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js
index 11fce6d..cf98b42c 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.js
@@ -22,6 +22,7 @@
 
   Polymer({
     is: 'gr-comment-thread',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the thread should be discarded.
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
index a576583..b5481a9 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.js
@@ -33,6 +33,7 @@
 
   Polymer({
     is: 'gr-comment',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the create fix comment action is triggered.
diff --git a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js
index b86b72a..4ac059d 100644
--- a/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js
+++ b/polygerrit-ui/app/elements/shared/gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-confirm-delete-comment-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.js b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.js
index cabee36..550f1df 100644
--- a/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.js
+++ b/polygerrit-ui/app/elements/shared/gr-copy-clipboard/gr-copy-clipboard.js
@@ -21,6 +21,7 @@
 
   Polymer({
     is: 'gr-copy-clipboard',
+    _legacyUndefinedCheck: true,
 
     properties: {
       text: String,
diff --git a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
index f750cd2..36e6a7b 100644
--- a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
+++ b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
@@ -24,6 +24,7 @@
 
   Polymer({
     is: 'gr-cursor-manager',
+    _legacyUndefinedCheck: true,
 
     properties: {
       stops: {
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
index 3417a0d..4d7f2bb 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
@@ -33,6 +33,7 @@
 
   Polymer({
     is: 'gr-date-formatter',
+    _legacyUndefinedCheck: true,
 
     properties: {
       dateStr: {
diff --git a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
index 6163b09..b8b2af4 100644
--- a/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
+++ b/polygerrit-ui/app/elements/shared/gr-dialog/gr-dialog.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-dialog',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the confirm button is pressed.
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js
index e2278c2..eb99d7a 100644
--- a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-diff-preferences',
+    _legacyUndefinedCheck: true,
 
     properties: {
       hasUnsavedChanges: {
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
index ca77a30..ed7c2cc 100644
--- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
+++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-download-commands',
+    _legacyUndefinedCheck: true,
     properties: {
       commands: Array,
       _loggedIn: {
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js
index 40d8811..bcf3729 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown-list/gr-dropdown-list.js
@@ -47,6 +47,7 @@
 
   Polymer({
     is: 'gr-dropdown-list',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the selected value changes
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
index 3d9d36b..6eb3108 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
@@ -22,6 +22,7 @@
 
   Polymer({
     is: 'gr-dropdown',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a non-link dropdown item with the given ID is tapped.
@@ -280,7 +281,9 @@
      */
     _resetCursorStops() {
       Polymer.dom.flush();
-      this._listElements = Polymer.dom(this.root).querySelectorAll('li');
+      // Polymer2: querySelectorAll returns NodeList instead of Array.
+      this._listElements = Array.from(
+          Polymer.dom(this.root).querySelectorAll('li'));
     },
 
     _computeHasTooltip(tooltip) {
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.js b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.js
index f567d39..827bb71 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.js
+++ b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content.js
@@ -22,6 +22,7 @@
 
   Polymer({
     is: 'gr-editable-content',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the save button is pressed.
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js
index 3514492..f23afea 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label.js
@@ -22,6 +22,7 @@
 
   Polymer({
     is: 'gr-editable-label',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when the value is changed.
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
index 2c32709..87cd4b4 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-fixed-panel',
+    _legacyUndefinedCheck: true,
 
     properties: {
       floatingDisabled: Boolean,
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
index 4e68d42..8836574 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text.js
@@ -22,6 +22,7 @@
 
   Polymer({
     is: 'gr-formatted-text',
+    _legacyUndefinedCheck: true,
 
     properties: {
       content: {
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js
index f9c1da1..498c590 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard/gr-hovercard.js
@@ -27,6 +27,7 @@
 
   Polymer({
     is: 'gr-hovercard',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /**
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
index 3593253..d8a662e 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html
@@ -38,6 +38,5 @@
   <script src="gr-plugin-endpoints.js"></script>
   <script src="gr-plugin-action-context.js"></script>
   <script src="gr-plugin-rest-api.js"></script>
-  <script src="gr-rest-api-hooks.js"></script>
   <script src="gr-public-js-api.js"></script>
 </dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
index f981c99..21fd9eb 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.js
@@ -38,6 +38,7 @@
 
   Polymer({
     is: 'gr-js-api-interface',
+    _legacyUndefinedCheck: true,
 
     properties: {
       _elements: {
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
index 7047229..b31c3f2 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
@@ -319,10 +319,6 @@
     return new GrSettingsApi(this);
   };
 
-  Plugin.prototype.restApiHooks = function() {
-    return new GrRestApiHooks(this);
-  };
-
   /**
    * To make REST requests for plugin-provided endpoints, use
    * @example
@@ -630,12 +626,6 @@
     return _allPluginsPromise;
   };
 
-  // TODO: Remove this. This is a hack to get the tests to pass.
-  // It would be much better to call GrRestApiHooks.pluginParams directly.
-  Gerrit._pluginParams = function(endpointName, initialParams) {
-    return GrRestApiHooks.pluginParams(endpointName, initialParams);
-  };
-
   Gerrit._pluginLoadingTimeout = function() {
     console.error(`Failed to load plugins: ${Object.keys(_pluginsPending)}`);
     Gerrit._setPluginsPending([]);
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-rest-api-hooks.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-rest-api-hooks.js
deleted file mode 100644
index 4e9e211..0000000
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-rest-api-hooks.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
- * @license
- * Copyright (C) 2019 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.
- */
-(function() {
-  'use strict';
-
-  // Prevent redefinition.
-  if (window.GrRestApiHooks) { return; }
-
-  // Stores a map of endpointNames and api instances to add parameters to
-  // REST API calls.
-  const _apiInstances = {};
-
-  function GrRestApiHooks(plugin) {
-    this.plugin = plugin;
-    // Stores a map of endpointNames and functions to add parameters to REST API
-    // calls.
-    this._addParameterFunctions = {};
-  }
-
-  /**
-   * Registers an api hook for a particular api endpoint.
-   * This is called by a plugin.
-   *
-   * @param {string} endpointName the name of the endpoint.
-   * @param {Function} addParameterFunction the function that returns params
-   * for the plugin. Takes in current params.
-   */
-  GrRestApiHooks.prototype.registerRestApiParams = function(endpointName,
-      addParameterFunction) {
-    if (this._addParameterFunctions[endpointName]) {
-      console.warn(`Rewriting rest api parameter function for
-        ${this.plugin.getPluginName()}`);
-    }
-    this._addParameterFunctions[endpointName] = addParameterFunction;
-    if (!_apiInstances[endpointName]) {
-      _apiInstances[endpointName] = [];
-    }
-    _apiInstances[endpointName].push(this);
-  };
-
-  /**
-   * Returns params for a registered api hook for a particular api endpoint or
-   * null.
-   * This is called by the application, not the plugin.
-   * It will either return params or null if there are no params.
-   * @param {string} endpointName the name of the endpoint.
-   * @param {!Object} initialParams the params of the rest api call.
-   */
-  GrRestApiHooks.prototype._getRestApiParams = function(endpointName,
-      initialParams) {
-    const addParameterFunction = this._addParameterFunctions[endpointName];
-    if (!addParameterFunction) return null;
-    return addParameterFunction(initialParams);
-  };
-
-  /**
-   * Gets the params for a particular mutation endpoint.
-   *
-   * This is called by the application and should not be called by plugins.
-   *
-   * @param {string} endpointName the name of the endpoint.
-   * @param {!Object} initialParams the params of the rest api call.
-   * @return new parameters to add to a REST API call.
-   */
-  GrRestApiHooks.pluginParams = function(endpointName, initialParams) {
-    return (_apiInstances[endpointName] || []).reduce((accum, apiInstance) => {
-      const pluginParams = apiInstance._getRestApiParams(
-          endpointName, initialParams);
-      if (pluginParams) {
-        accum[apiInstance.plugin.getPluginName()] =
-          JSON.stringify(pluginParams);
-      }
-      return accum;
-    }, {});
-  };
-
-  window.GrRestApiHooks = GrRestApiHooks;
-})(window);
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
index 40edc28..2036f2a 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-label-info',
+    _legacyUndefinedCheck: true,
 
     properties: {
       labelInfo: Object,
diff --git a/polygerrit-ui/app/elements/shared/gr-label/gr-label.js b/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
index 0de0881..c437885 100644
--- a/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
+++ b/polygerrit-ui/app/elements/shared/gr-label/gr-label.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-label',
+    _legacyUndefinedCheck: true,
 
     behaviors: [
       Gerrit.TooltipBehavior,
diff --git a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.js b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.js
index 54b38eb..a892522 100644
--- a/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-labeled-autocomplete/gr-labeled-autocomplete.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-labeled-autocomplete',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a value is chosen.
diff --git a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js
index 478c619..ba0ab1f 100644
--- a/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js
+++ b/polygerrit-ui/app/elements/shared/gr-lib-loader/gr-lib-loader.js
@@ -22,6 +22,7 @@
 
   Polymer({
     is: 'gr-lib-loader',
+    _legacyUndefinedCheck: true,
 
     properties: {
       _hljsState: {
diff --git a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js
index 0dc3a7d..44a8791 100644
--- a/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-limited-text/gr-limited-text.js
@@ -26,6 +26,7 @@
 
   Polymer({
     is: 'gr-limited-text',
+    _legacyUndefinedCheck: true,
 
     properties: {
       /** The un-truncated text to display. */
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js
index f8f29b8..8388a07 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-linked-chip',
+    _legacyUndefinedCheck: true,
 
     properties: {
       href: String,
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
index 530da02..b1bd5cd 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-linked-text',
+    _legacyUndefinedCheck: true,
 
     properties: {
       removeZeroWidthSpace: Boolean,
diff --git a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.js b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.js
index 8b83eb3..53d05e1 100644
--- a/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.js
+++ b/polygerrit-ui/app/elements/shared/gr-list-view/gr-list-view.js
@@ -21,6 +21,7 @@
 
   Polymer({
     is: 'gr-list-view',
+    _legacyUndefinedCheck: true,
 
     properties: {
       createNew: Boolean,
diff --git a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
index 6df04a2..c167b3b 100644
--- a/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
+++ b/polygerrit-ui/app/elements/shared/gr-overlay/gr-overlay.js
@@ -23,6 +23,7 @@
 
   Polymer({
     is: 'gr-overlay',
+    _legacyUndefinedCheck: true,
 
     /**
      * Fired when a fullscreen overlay is closed
diff --git a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.js b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.js
index 9ccff600..0962540 100644
--- a/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.js
+++ b/polygerrit-ui/app/elements/shared/gr-page-nav/gr-page-nav.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-page-nav',
+    _legacyUndefinedCheck: true,
 
     properties: {
       _headerHeight: Number,
diff --git a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.js b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.js
index e2298c3..2fccc8d 100644
--- a/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.js
+++ b/polygerrit-ui/app/elements/shared/gr-repo-branch-picker/gr-repo-branch-picker.js
@@ -22,6 +22,7 @@
 
   Polymer({
     is: 'gr-repo-branch-picker',
+    _legacyUndefinedCheck: true,
 
     properties: {
       repo: {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
index 4889472..562980c 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
@@ -30,8 +30,5 @@
   <!-- NB: Order is important, because of namespaced classes. -->
   <script src="gr-auth.js"></script>
   <script src="gr-reviewer-updates-parser.js"></script>
-  <script src="../gr-js-api-interface/gr-plugin-endpoints.js"></script>
-  <script src="../gr-js-api-interface/gr-rest-api-hooks.js"></script>
-  <script src="../gr-js-api-interface/gr-public-js-api.js"></script>
   <script src="gr-rest-api-interface.js"></script>
 </dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 03a58074..4b66a6e 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -183,6 +183,7 @@
 
   Polymer({
     is: 'gr-rest-api-interface',
+    _legacyUndefinedCheck: true,
 
     behaviors: [
       Gerrit.PathListBehavior,
@@ -1246,51 +1247,48 @@
      *     changeInfos.
      */
     getChanges(opt_changesPerPage, opt_query, opt_offset, opt_options) {
-      return Gerrit.awaitPluginsLoaded().then(() => {
-        const options = opt_options || this.listChangesOptionsToHex(
-            this.ListChangesOption.LABELS,
-            this.ListChangesOption.DETAILED_ACCOUNTS
-        );
-        // Issue 4524: respect legacy token with max sortkey.
-        if (opt_offset === 'n,z') {
-          opt_offset = 0;
+      const options = opt_options || this.listChangesOptionsToHex(
+          this.ListChangesOption.LABELS,
+          this.ListChangesOption.DETAILED_ACCOUNTS
+      );
+      // Issue 4524: respect legacy token with max sortkey.
+      if (opt_offset === 'n,z') {
+        opt_offset = 0;
+      }
+      const params = {
+        O: options,
+        S: opt_offset || 0,
+      };
+      if (opt_changesPerPage) { params.n = opt_changesPerPage; }
+      if (opt_query && opt_query.length > 0) {
+        params.q = opt_query;
+      }
+      const iterateOverChanges = arr => {
+        for (const change of (arr || [])) {
+          this._maybeInsertInLookup(change);
         }
-        const params = {
-          O: options,
-          S: opt_offset || 0,
-        };
-        if (opt_changesPerPage) { params.n = opt_changesPerPage; }
-        if (opt_query && opt_query.length > 0) {
-          params.q = opt_query;
+      };
+      const req = {
+        url: '/changes/',
+        params,
+        reportUrlAsIs: true,
+      };
+      return this._fetchJSON(req).then(response => {
+        // Response may be an array of changes OR an array of arrays of
+        // changes.
+        if (opt_query instanceof Array) {
+          // Normalize the response to look like a multi-query response
+          // when there is only one query.
+          if (opt_query.length === 1) {
+            response = [response];
+          }
+          for (const arr of response) {
+            iterateOverChanges(arr);
+          }
+        } else {
+          iterateOverChanges(response);
         }
-        const iterateOverChanges = arr => {
-          for (const change of (arr || [])) {
-            this._maybeInsertInLookup(change);
-          }
-        };
-        Object.assign(params, Gerrit._pluginParams('changes', params));
-        const req = {
-          url: '/changes/',
-          params,
-          reportUrlAsIs: true,
-        };
-        return this._fetchJSON(req).then(response => {
-          // Response may be an array of changes OR an array of arrays of
-          // changes.
-          if (opt_query instanceof Array) {
-            // Normalize the response to look like a multi-query response
-            // when there is only one query.
-            if (opt_query.length === 1) {
-              response = [response];
-            }
-            for (const arr of response) {
-              iterateOverChanges(arr);
-            }
-          } else {
-            iterateOverChanges(response);
-          }
-          return response;
-        });
+        return response;
       });
     },
 
@@ -1371,15 +1369,9 @@
      * @param {function()=} opt_cancelCondition
      */
     _getChangeDetail(changeNum, optionsHex, opt_errFn, opt_cancelCondition) {
-      return Promise.all([
-        Gerrit.awaitPluginsLoaded(),
-        this.getChangeActionURL(changeNum, null, '/detail'),
-      ]).then(([_, url]) => {
+      return this.getChangeActionURL(changeNum, null, '/detail').then(url => {
         const urlWithParams = this._urlWithParams(url, optionsHex);
-
         const params = {O: optionsHex};
-        Object.assign(params, Gerrit._pluginParams('change', params));
-
         const req = {
           url,
           errFn: opt_errFn,
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index a8b00c6..ef4e401 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -55,8 +55,6 @@
           return Promise.resolve(testJSON);
         },
       }));
-      sandbox.stub(Gerrit, 'awaitPluginsLoaded').returns(
-          Promise.resolve(true));
     });
 
     teardown(() => {
@@ -524,13 +522,11 @@
           });
     });
 
-    test('legacy n,z key in change url is replaced', done => {
+    test('legacy n,z key in change url is replaced', () => {
       const stub = sandbox.stub(element, '_fetchJSON')
           .returns(Promise.resolve([]));
-      element.getChanges(1, null, 'n,z').then(() => {
-        assert.equal(stub.lastCall.args[0].params.S, 0);
-        done();
-      }, done);
+      element.getChanges(1, null, 'n,z');
+      assert.equal(stub.lastCall.args[0].params.S, 0);
     });
 
     test('saveDiffPreferences invalidates cache line', () => {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html
index 0a1b14e..05c2cee 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html
@@ -153,6 +153,7 @@
 
       Polymer({
         is: 'mock-diff-response',
+        _legacyUndefinedCheck: true,
         properties: {
           diffResponse: {
             type: Object,
diff --git a/polygerrit-ui/app/elements/shared/gr-select/gr-select.js b/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
index b732fa5..85e1a61 100644
--- a/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
+++ b/polygerrit-ui/app/elements/shared/gr-select/gr-select.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-select',
+    _legacyUndefinedCheck: true,
     properties: {
       bindValue: {
         type: String,
diff --git a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.js b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.js
index 2c546cc..901b8ce 100644
--- a/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.js
+++ b/polygerrit-ui/app/elements/shared/gr-shell-command/gr-shell-command.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-shell-command',
+    _legacyUndefinedCheck: true,
 
     properties: {
       command: String,
diff --git a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
index c425609..6146e0f 100644
--- a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
+++ b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage.js
@@ -30,6 +30,7 @@
 
   Polymer({
     is: 'gr-storage',
+    _legacyUndefinedCheck: true,
 
     properties: {
       _lastCleanup: Number,
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
index a3da7d8..7929fbe 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
@@ -54,6 +54,7 @@
 
   Polymer({
     is: 'gr-textarea',
+    _legacyUndefinedCheck: true,
 
     /**
      * @event bind-value-changed
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.js b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.js
index c5de8f4..b46cafb 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.js
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-tooltip-content',
+    _legacyUndefinedCheck: true,
 
     properties: {
       title: {
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js
index fb87b558..3e16beb 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip.js
@@ -19,6 +19,7 @@
 
   Polymer({
     is: 'gr-tooltip',
+    _legacyUndefinedCheck: true,
 
     properties: {
       text: String,
diff --git a/polygerrit-ui/app/rules.bzl b/polygerrit-ui/app/rules.bzl
index 7788e5f..3012f7f 100644
--- a/polygerrit-ui/app/rules.bzl
+++ b/polygerrit-ui/app/rules.bzl
@@ -22,6 +22,8 @@
         deps = [name + "_closure_lib"],
     )
 
+    # TODO(davido): Remove JSC_REFERENCE_BEFORE_DECLARE when this is fixed upstream:
+    # https://github.com/Polymer/polymer-resin/issues/7
     closure_js_library(
         name = name + "_closure_lib",
         srcs = [appName + ".js"],
@@ -30,6 +32,7 @@
         # and remove this supression
         suppress = [
             "JSC_JSDOC_MISSING_TYPE_WARNING",
+            "JSC_REFERENCE_BEFORE_DECLARE",
             "JSC_UNNECESSARY_ESCAPE",
             "JSC_UNUSED_LOCAL_ASSIGNMENT",
         ],
diff --git a/polygerrit-ui/app/samples/bind-parameters.html b/polygerrit-ui/app/samples/bind-parameters.html
index dc7a87a..a7eb39a 100644
--- a/polygerrit-ui/app/samples/bind-parameters.html
+++ b/polygerrit-ui/app/samples/bind-parameters.html
@@ -15,6 +15,7 @@
   <script>
     Polymer({
       is: 'my-bind-sample',
+      _legacyUndefinedCheck: true,
       properties: {
         computedExample: {
           type: String,
diff --git a/polygerrit-ui/app/samples/repo-command.html b/polygerrit-ui/app/samples/repo-command.html
index 67e528a..37aca04 100644
--- a/polygerrit-ui/app/samples/repo-command.html
+++ b/polygerrit-ui/app/samples/repo-command.html
@@ -29,6 +29,7 @@
   <script>
     Polymer({
       is: 'repo-command-low',
+      _legacyUndefinedCheck: true,
       attached() {
         console.log(this.repoName);
         console.log(this.config);
diff --git a/polygerrit-ui/app/samples/some-screen.html b/polygerrit-ui/app/samples/some-screen.html
index de29315..527ebce 100644
--- a/polygerrit-ui/app/samples/some-screen.html
+++ b/polygerrit-ui/app/samples/some-screen.html
@@ -38,6 +38,7 @@
   <script>
     Polymer({
       is: 'some-screen-main',
+      _legacyUndefinedCheck: true,
       properties: {
         rootUrl: String,
       },
diff --git a/polygerrit-ui/app/styles/themes/app-theme.html b/polygerrit-ui/app/styles/themes/app-theme.html
index 8fc6e20..a4a2ca4 100644
--- a/polygerrit-ui/app/styles/themes/app-theme.html
+++ b/polygerrit-ui/app/styles/themes/app-theme.html
@@ -14,8 +14,8 @@
 See the License for the specific language governing permissions and
 limitations under the License.
 -->
-<style is="custom-style">
-:root {
+<custom-style><style is="custom-style">
+html {
   /* Following vars have LTS for plugin API. */
   --primary-text-color: #000;
   --header-background-color: #eee;
@@ -137,8 +137,8 @@
   --reply-overlay-z-index: 1000;
 }
 @media screen and (max-width: 50em) {
-  :root {
+  html {
     --default-horizontal-margin: .7rem;
   }
 }
-</style>
+</style></custom-style>
diff --git a/polygerrit-ui/app/styles/themes/dark-theme.html b/polygerrit-ui/app/styles/themes/dark-theme.html
index f22ec5d..d5db416 100644
--- a/polygerrit-ui/app/styles/themes/dark-theme.html
+++ b/polygerrit-ui/app/styles/themes/dark-theme.html
@@ -1,5 +1,5 @@
 <dom-module id="dark-theme">
-  <style is="custom-style">
+  <custom-style><style is="custom-style">
     html {
       --primary-text-color: #e2e2e2;
       --view-background-color: #212121;
@@ -84,5 +84,5 @@
 
       background-color: var(--view-background-color);
     }
-  </style>
+  </style></custom-style>
 </dom-module>
diff --git a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
index e4f699c..85f338c 100644
--- a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
+++ b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
@@ -24,6 +24,7 @@
   {@param? faviconPath: ?}
   {@param? versionInfo: ?}
   {@param? deprecateGwtUi: ?}
+  {@param? polymer2: ?}
   <!DOCTYPE html>{\n}
   <html lang="en">{\n}
   <meta charset="utf-8">{\n}
@@ -42,6 +43,7 @@
     {if $versionInfo}window.VERSION_INFO = '{$versionInfo}';{/if}
     {if $staticResourcePath != ''}window.STATIC_RESOURCE_PATH = '{$staticResourcePath}';{/if}
     {if $assetsPath}window.ASSETS_PATH = '{$assetsPath}';{/if}
+    {if $polymer2}window.POLYMER2 = true;{/if}
   </script>{\n}
 
   {if $faviconPath}
diff --git a/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy b/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
index 55e6ef5..b619c53 100644
--- a/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
+++ b/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
@@ -36,7 +36,7 @@
   {if $email.changeUrl}
     <div itemscope itemtype="http://schema.org/EmailMessage">
       <div itemscope itemprop="action" itemtype="http://schema.org/ViewAction">
-        <link itemprop="url" href="{$email.changeUrl |blessStringAsTrustedResourceUrlForLegacy}"/>
+        <link itemprop="url" href="{$email.changeUrl}"/>
         <meta itemprop="name" content="View Change"/>
       </div>
     </div>
diff --git a/resources/com/google/gerrit/server/mime/mime-types.properties b/resources/com/google/gerrit/server/mime/mime-types.properties
index e4d478e..8159ac1 100644
--- a/resources/com/google/gerrit/server/mime/mime-types.properties
+++ b/resources/com/google/gerrit/server/mime/mime-types.properties
@@ -209,6 +209,7 @@
 sql = text/x-sql
 ss = text/x-scheme
 st = text/x-stsrc
+star = text/x-python
 stex = text/x-stex
 sv = text/x-systemverilog
 svh = text/x-systemverilog
diff --git a/tools/bzl/js.bzl b/tools/bzl/js.bzl
index dd48693..83c13a3 100644
--- a/tools/bzl/js.bzl
+++ b/tools/bzl/js.bzl
@@ -472,7 +472,7 @@
 
     closure_js_binary(
         name = name + "_bin",
-        compilation_level = "SIMPLE",
+        compilation_level = "WHITESPACE_ONLY",
         defs = [
             "--polymer_version=1",
             "--language_out=ECMASCRIPT6",
diff --git a/tools/eclipse/BUILD b/tools/eclipse/BUILD
index 6b475d8..5ef3a46 100644
--- a/tools/eclipse/BUILD
+++ b/tools/eclipse/BUILD
@@ -10,6 +10,9 @@
 TEST_DEPS = [
     "//javatests/com/google/gerrit/elasticsearch:elasticsearch_test_utils",
     "//javatests/com/google/gerrit/server:server_tests",
+]
+
+TEST_DEPS_GENERATED = [
     "//proto/testing:test_java_proto",
 ]
 
@@ -27,13 +30,13 @@
 java_library(
     name = "classpath",
     testonly = True,
-    runtime_deps = LIBS + PGMLIBS + DEPS,
+    runtime_deps = LIBS + PGMLIBS + DEPS + TEST_DEPS_GENERATED,
 )
 
 classpath_collector(
     name = "main_classpath_collect",
     testonly = True,
-    deps = LIBS + PGMLIBS + DEPS + TEST_DEPS +
+    deps = LIBS + PGMLIBS + DEPS + TEST_DEPS + TEST_DEPS_GENERATED +
            ["//plugins/%s:%s__plugin" % (n, n) for n in CORE_PLUGINS + CUSTOM_PLUGINS] +
            ["//plugins/%s:%s__plugin_test_deps" % (n, n) for n in CUSTOM_PLUGINS_TEST_DEPS],
 )
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
index 24d4733..46aeb31 100755
--- a/tools/eclipse/project.py
+++ b/tools/eclipse/project.py
@@ -222,7 +222,10 @@
 
         p = path.join(s, 'java')
         if path.exists(p):
-            classpathentry('src', p, out=out)
+            classpathentry('src', p, out=out + '/main')
+            p = path.join(s, 'javatests')
+            if path.exists(p):
+                classpathentry('src', p, out=out + '/test')
             continue
 
         for env in ['main', 'test']:
diff --git a/tools/maven/gerrit-acceptance-framework_pom.xml b/tools/maven/gerrit-acceptance-framework_pom.xml
index 6a8d53d..65aa8b9 100644
--- a/tools/maven/gerrit-acceptance-framework_pom.xml
+++ b/tools/maven/gerrit-acceptance-framework_pom.xml
@@ -2,7 +2,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-acceptance-framework</artifactId>
-  <version>3.0-SNAPSHOT</version>
+  <version>3.0.0-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>Gerrit Code Review - Acceptance Test Framework</name>
   <description>Framework for Gerrit's acceptance tests</description>
diff --git a/tools/maven/gerrit-extension-api_pom.xml b/tools/maven/gerrit-extension-api_pom.xml
index 363b915..09299be 100644
--- a/tools/maven/gerrit-extension-api_pom.xml
+++ b/tools/maven/gerrit-extension-api_pom.xml
@@ -2,7 +2,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-extension-api</artifactId>
-  <version>3.0-SNAPSHOT</version>
+  <version>3.0.0-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>Gerrit Code Review - Extension API</name>
   <description>API for Gerrit Extensions</description>
diff --git a/tools/maven/gerrit-plugin-api_pom.xml b/tools/maven/gerrit-plugin-api_pom.xml
index 7ef1276..b13aa3a 100644
--- a/tools/maven/gerrit-plugin-api_pom.xml
+++ b/tools/maven/gerrit-plugin-api_pom.xml
@@ -2,7 +2,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-plugin-api</artifactId>
-  <version>3.0-SNAPSHOT</version>
+  <version>3.0.0-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>Gerrit Code Review - Plugin API</name>
   <description>API for Gerrit Plugins</description>
diff --git a/tools/maven/gerrit-war_pom.xml b/tools/maven/gerrit-war_pom.xml
index 7249bfc..791344e 100644
--- a/tools/maven/gerrit-war_pom.xml
+++ b/tools/maven/gerrit-war_pom.xml
@@ -2,7 +2,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-war</artifactId>
-  <version>3.0-SNAPSHOT</version>
+  <version>3.0.0-SNAPSHOT</version>
   <packaging>war</packaging>
   <name>Gerrit Code Review - WAR</name>
   <description>Gerrit WAR</description>
diff --git a/version.bzl b/version.bzl
index 20fd8a7..a1275f2 100644
--- a/version.bzl
+++ b/version.bzl
@@ -2,4 +2,4 @@
 # Used by :api_install and :api_deploy targets
 # when talking to the destination repository.
 #
-GERRIT_VERSION = "3.0-SNAPSHOT"
+GERRIT_VERSION = "3.0.0-SNAPSHOT"