diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2f454fe5907ea2c6b6da41cd73e89b055ee529c5..39d15c9c54cf933e04bd2827d8175ff91adee090 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,7 +20,7 @@
 
 cmake_minimum_required(VERSION 3.13)
 
-set(libcaosdb_VERSION 0.0.8)
+set(libcaosdb_VERSION 0.0.9)
 set(libcaosdb_COMPATIBLE_SERVER_VERSION_MAJOR 0)
 set(libcaosdb_COMPATIBLE_SERVER_VERSION_MINOR 5)
 set(libcaosdb_COMPATIBLE_SERVER_VERSION_PATCH 0)
diff --git a/conanfile.py b/conanfile.py
index 0e922bc74936d4341c8a574220c6149459ac6b7e..e2133cea76ce826d168364259d6a519ba28a75d3 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -3,7 +3,7 @@ from conans import ConanFile, CMake, tools
 
 class CaosdbConan(ConanFile):
     name = "caosdb"
-    version = "0.0.8"
+    version = "0.0.9"
     license = "AGPL-3.0-or-later"
     author = "Timm C. Fitschen <t.fitschen@indiscale.com>"
     url = "https://gitlab.indiscale.com/caosdb/src/caosdb-cpplib.git"
diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h
index 4249b91b275902a5b155484922b552d0d5238d67..11b425f3f33fecfabc2a63fdaf001a1dd5fb92e1 100644
--- a/include/caosdb/entity.h
+++ b/include/caosdb/entity.h
@@ -374,6 +374,9 @@ private:
 class Entity {
 public:
   Entity();
+  inline Entity(const Entity &original) : Entity(CreateProtoEntity()) {
+    this->wrapped->CopyFrom(*original.wrapped);
+  };
   explicit Entity(IdResponse *idResponse);
   explicit inline Entity(ProtoEntity *wrapped) : wrapped(wrapped) {
     errors.wrapped = this->wrapped->mutable_errors();
@@ -383,9 +386,12 @@ public:
     parents.wrapped = this->wrapped->mutable_parents();
   };
 
-  [[nodiscard]] inline auto GetId() const -> const std::string & {
+  [[nodiscard]] inline auto GetId() const noexcept -> const std::string & {
     return wrapped->id();
   };
+  [[nodiscard]] inline auto HasId() const noexcept -> bool {
+    return !wrapped->id().empty();
+  }
   [[nodiscard]] inline auto GetVersionId() const -> const std::string & {
     return wrapped->version().id();
   };
@@ -435,9 +441,7 @@ public:
   }
 
   auto SetRole(const std::string &role) -> void;
-  auto SetId(const std::string &id) -> void;
   auto SetName(const std::string &name) -> void;
-  auto SetVersionId(const std::string &id) -> void;
 
   auto SetValue(const std::string &value) -> void;
   auto SetUnit(const std::string &unit) -> void;
@@ -451,8 +455,10 @@ public:
    */
   auto CopyTo(ProtoEntity *target) -> void;
 
-private:
+protected:
   static auto CreateProtoEntity() -> ProtoEntity *;
+  auto SetId(const std::string &id) -> void;
+  auto SetVersionId(const std::string &id) -> void;
   ProtoEntity *wrapped;
   Properties properties;
   Parents parents;
diff --git a/include/caosdb/exceptions.h b/include/caosdb/exceptions.h
index d60f7a162b6a36d803a37c1e40dfc43d21a3f172..9ac46d2fd2e0a24fab08c49606b21033893c14e7 100644
--- a/include/caosdb/exceptions.h
+++ b/include/caosdb/exceptions.h
@@ -73,10 +73,22 @@ public:
     : Exception(StatusCode::GENERIC_TRANSACTION_ERROR, what_arg) {}
 };
 
-class TransactionStatusError : public TransactionError {
+/**
+ * @brief The transaction is in the wrong state for your action.
+ */
+class TransactionStatusError : public Exception {
 public:
   explicit TransactionStatusError(const std::string &what_arg)
-    : TransactionError(StatusCode::TRANSACTION_STATUS_ERROR, what_arg) {}
+    : Exception(StatusCode::TRANSACTION_STATUS_ERROR, what_arg) {}
+};
+
+/**
+ * @brief The transaction has a wrong type for your action.
+ */
+class TransactionTypeError : public Exception {
+public:
+  explicit TransactionTypeError(const std::string &what_arg)
+    : Exception(StatusCode::TRANSACTION_TYPE_ERROR, what_arg) {}
 };
 
 /**
diff --git a/include/caosdb/status_code.h b/include/caosdb/status_code.h
index 26f4abd4ea4c8c256f66e23268dcac744e4db3c0..a6fcda905284ae9dfa9b7e46c6be8da36bb32272 100644
--- a/include/caosdb/status_code.h
+++ b/include/caosdb/status_code.h
@@ -36,6 +36,8 @@ namespace caosdb {
  * transaction).
  */
 enum StatusCode {
+  READY = -4,
+  GO_ON = -3,
   INITIAL = -2,
   EXECUTING = -1,
   SUCCESS = 0,
@@ -49,6 +51,7 @@ enum StatusCode {
   TRANSACTION_STATUS_ERROR = 25,
   TRANSACTION_TYPE_ERROR = 26,
   UNSUPPORTED_FEATURE = 27,
+  ORIGINAL_ENTITY_MISSING_ID = 28,
 };
 
 auto get_status_description(int code) -> const std::string &;
diff --git a/include/caosdb/transaction.h b/include/caosdb/transaction.h
index 9d957f08e3f2db4d7e4fb0e52d8351629c2c0f94..48762db4396e8996442ef5be287e3965836ecd4e 100644
--- a/include/caosdb/transaction.h
+++ b/include/caosdb/transaction.h
@@ -31,32 +31,28 @@
 #include "caosdb/transaction_status.h"           // for TransactionStatus
 #include "caosdb/status_code.h"                  // for StatusCode
 #include "google/protobuf/util/json_util.h" // for MessageToJsonString, Jso...
-#include <stdexcept>
 #include <iterator>
 // IWYU pragma: no_include <ext/alloc_traits.h>
 #include <memory> // for shared_ptr, unique_ptr
+#include <stdexcept>
 #include <string> // for string
 #include <vector> // for vector
 
-/**
- * @brief Creation and execution of transactions.
- * @author Timm Fitschen
- * @date 2021-08-05
- */
-namespace caosdb::transaction {
-
 /**
  * Do all necessary checks and assure that another retrieval (by id or by
  * query) can be added as a sub-request to a transaction.
  */
 #define ASSERT_CAN_ADD_RETRIEVAL                                               \
-  if (!IsStatus(TransactionStatus::INITIAL())) {                               \
+  if (!IsStatus(TransactionStatus::INITIAL()) &&                               \
+      !IsStatus(TransactionStatus::GO_ON())) {                                 \
     return StatusCode::TRANSACTION_STATUS_ERROR;                               \
   }                                                                            \
   switch (this->transaction_type) {                                            \
   case NONE:                                                                   \
     this->transaction_type = TransactionType::READ_ONLY;                       \
+    break;                                                                     \
   case READ_ONLY:                                                              \
+    break;                                                                     \
   case MIXED_READ_AND_WRITE:                                                   \
     break;                                                                     \
   default:                                                                     \
@@ -66,12 +62,26 @@ namespace caosdb::transaction {
       "wrong TransactionType.")                                                \
   }
 
+/**
+ * Do all necessary checks and assure that another retrieval (by id or by
+ * query) can be added as a sub-request to a transaction.
+ */
+#define ASSERT_CAN_ADD_QUERY                                                   \
+  ASSERT_CAN_ADD_RETRIEVAL                                                     \
+  if (this->has_query) {                                                       \
+    CAOSDB_LOG_ERROR_AND_RETURN_STATUS(                                        \
+      logger_name, StatusCode::UNSUPPORTED_FEATURE,                            \
+      "Currently the number of queries which can be processed in a single "    \
+      "transaction is limitted to one.");                                      \
+  }
+
 /**
  * Do all necessary checks and assure that another deletion can be added as a
  * sub-request to a transaction.
  */
 #define ASSERT_CAN_ADD_DELETION                                                \
-  if (!IsStatus(TransactionStatus::INITIAL())) {                               \
+  if (!IsStatus(TransactionStatus::INITIAL()) &&                               \
+      !IsStatus(TransactionStatus::GO_ON())) {                                 \
     return StatusCode::TRANSACTION_STATUS_ERROR;                               \
   }                                                                            \
   switch (this->transaction_type) {                                            \
@@ -93,7 +103,8 @@ namespace caosdb::transaction {
  * sub-request to a transaction.
  */
 #define ASSERT_CAN_ADD_INSERTION                                               \
-  if (!IsStatus(TransactionStatus::INITIAL())) {                               \
+  if (!IsStatus(TransactionStatus::INITIAL()) &&                               \
+      !IsStatus(TransactionStatus::GO_ON())) {                                 \
     return StatusCode::TRANSACTION_STATUS_ERROR;                               \
   }                                                                            \
   switch (this->transaction_type) {                                            \
@@ -110,6 +121,42 @@ namespace caosdb::transaction {
       "wrong TransactionType.")                                                \
   }
 
+/**
+ * Do all necessary checks and assure that another update can be added as a
+ * sub-request to a transaction.
+ */
+#define ASSERT_CAN_ADD_UPDATE                                                  \
+  if (!IsStatus(TransactionStatus::INITIAL()) &&                               \
+      !IsStatus(TransactionStatus::GO_ON())) {                                 \
+    return StatusCode::TRANSACTION_STATUS_ERROR;                               \
+  }                                                                            \
+  switch (this->transaction_type) {                                            \
+  case NONE:                                                                   \
+    this->transaction_type = TransactionType::INSERT;                          \
+  case INSERT:                                                                 \
+  case MIXED_WRITE:                                                            \
+  case MIXED_READ_AND_WRITE:                                                   \
+    break;                                                                     \
+  default:                                                                     \
+    CAOSDB_LOG_ERROR_AND_RETURN_STATUS(                                        \
+      logger_name, StatusCode::TRANSACTION_TYPE_ERROR,                         \
+      "You cannot add an update to this transaction because it has the "       \
+      "wrong TransactionType.")                                                \
+  }                                                                            \
+  if (!entity->HasId()) {                                                      \
+    CAOSDB_LOG_ERROR_AND_RETURN_STATUS(                                        \
+      logger_name, StatusCode::ORIGINAL_ENTITY_MISSING_ID,                     \
+      "You cannot update this entity without any id. Probably you did not "    \
+      "retrieve it first? Entity updates should always start with the "        \
+      "retrieval of the existing entity which may then be changed.")           \
+  }
+
+/**
+ * @brief Creation and execution of transactions.
+ * @author Timm Fitschen
+ * @date 2021-08-05
+ */
+namespace caosdb::transaction {
 using caosdb::entity::Entity;
 using ProtoEntity = caosdb::entity::v1alpha1::Entity;
 using caosdb::entity::v1alpha1::EntityTransactionService;
@@ -128,10 +175,28 @@ static const std::string logger_name = "caosdb::transaction";
  * Abstract base class for the results of a Transaction.
  */
 class ResultSet {
+  class iterator;
+
 public:
   virtual ~ResultSet() = default;
   [[nodiscard]] virtual auto Size() const noexcept -> int = 0;
   [[nodiscard]] virtual auto At(const int index) const -> const Entity & = 0;
+  auto begin() const -> iterator;
+  auto end() const -> iterator;
+
+private:
+  class iterator : public std::iterator<std::output_iterator_tag, Entity> {
+  public:
+    explicit iterator(const ResultSet *result_set, int index = 0);
+    auto operator*() const -> const Entity &;
+    auto operator++() -> iterator &;
+    auto operator++(int) -> iterator;
+    auto operator!=(const iterator &rhs) const -> bool;
+
+  private:
+    int current_index = 0;
+    const ResultSet *result_set;
+  };
 };
 
 /**
@@ -236,6 +301,16 @@ public:
    */
   auto InsertEntity(Entity *entity) noexcept -> StatusCode;
 
+  /**
+   * Add the entity to this transaction for an update.
+   *
+   * The update is being processed when the Execute() or
+   * ExecuteAsynchronously() methods of this transaction are called.
+   *
+   * Changing the entity afterwards results in undefined behavior.
+   */
+  auto UpdateEntity(Entity *entity) noexcept -> StatusCode;
+
   /**
    * Add an entity id to this transaction for deletion.
    *
@@ -300,6 +375,7 @@ public:
   }
 
 private:
+  bool has_query = false;
   TransactionType transaction_type = TransactionType::NONE;
   mutable std::unique_ptr<ResultSet> result_set;
   mutable TransactionStatus status = TransactionStatus::INITIAL();
@@ -322,7 +398,8 @@ inline auto Transaction::RetrieveById(InputIterator begin,
     next = std::next(next);
   }
 
-  return StatusCode::INITIAL;
+  this->status = TransactionStatus::GO_ON();
+  return this->status.GetCode();
 }
 
 } // namespace caosdb::transaction
diff --git a/include/caosdb/transaction_status.h b/include/caosdb/transaction_status.h
index 56c6b005112fd15c76f3c2741803a58837baed9e..6f2f5cefd7b36f8c986726b4955f53381339f0a2 100644
--- a/include/caosdb/transaction_status.h
+++ b/include/caosdb/transaction_status.h
@@ -33,6 +33,18 @@ using caosdb::exceptions::AuthenticationError;
 using caosdb::exceptions::ConnectionError;
 using caosdb::exceptions::Exception;
 using caosdb::exceptions::TransactionError;
+using caosdb::exceptions::TransactionStatusError;
+using caosdb::exceptions::TransactionTypeError;
+
+/**
+ * Define static factory method in the TransactionStatus class.
+ */
+#define CAOSDB_TRANSACTION_STATUS_DEFAULT_FACTORY(_StatusName, _StatusCode)    \
+  inline static auto _StatusName()->const TransactionStatus & {                \
+    static const TransactionStatus instance(                                   \
+      _StatusCode, caosdb::get_status_description(_StatusCode));               \
+    return instance;                                                           \
+  }
 
 /**
  * TransactionStatus indicates the current status of a transaction and, when it
@@ -44,41 +56,62 @@ using caosdb::exceptions::TransactionError;
  */
 class TransactionStatus {
 public:
-  // REFACTORING NEEDED: When you touch this code again consider writing a
-  // macro, because this is a lot of redundant code here...
-  inline static auto INITIAL() -> const TransactionStatus & {
-    static const TransactionStatus initial(
-      StatusCode::INITIAL, caosdb::get_status_description(StatusCode::INITIAL));
-    return initial;
-  }
-  inline static auto EXECUTING() -> const TransactionStatus & {
-    static const TransactionStatus executing(
-      StatusCode::EXECUTING,
-      caosdb::get_status_description(StatusCode::EXECUTING));
-    return executing;
-  }
-  inline static auto SUCCESS() -> const TransactionStatus & {
-    static const TransactionStatus success(
-      StatusCode::SUCCESS, caosdb::get_status_description(StatusCode::SUCCESS));
-    return success;
-  }
-  inline static auto RPC_ERROR(const std::string &details)
-    -> const TransactionStatus {
-    // We use the GENERIC_RPC_ERROR here because we might want to add further
-    // RPC_ERROR states with different error codes (which stem from GRPC) here
-    // in the future.
-    return TransactionStatus(
-      StatusCode::GENERIC_RPC_ERROR,
-      caosdb::get_status_description(StatusCode::GENERIC_RPC_ERROR) +
-        " Original error: " + details);
-  }
-  inline static auto CONNECTION_ERROR() -> const TransactionStatus & {
-    static const TransactionStatus connection_error(
-      StatusCode::CONNECTION_ERROR,
-      caosdb::get_status_description(StatusCode::CONNECTION_ERROR));
-
-    return connection_error;
-  }
+  /**
+   * Factory for an INITIAL status.
+   *
+   * This status means that the transaction has not been executed yet and the
+   * transaction does not contain any sub-transactions yet.
+   */
+  CAOSDB_TRANSACTION_STATUS_DEFAULT_FACTORY(INITIAL, StatusCode::INITIAL)
+  /**
+   * Factory for a GO_ON status.
+   *
+   * This status means that the transaction has not been executed yet but it
+   * already contains sub-transaction and more subtransactions may be added.
+   *
+   * However, it also can be executed right now.
+   */
+  CAOSDB_TRANSACTION_STATUS_DEFAULT_FACTORY(GO_ON, StatusCode::GO_ON)
+  /**
+   * Factory for a READY status.
+   *
+   * This status means that the transaction has not been executed yet but it is
+   * ready to be executed and it is not possible anymore to add further
+   * sub-transactions.
+   */
+  CAOSDB_TRANSACTION_STATUS_DEFAULT_FACTORY(READY, StatusCode::READY)
+  /**
+   * Factory for an EXECUTING status.
+   *
+   * This status means that the transaction is currently being executed.
+   */
+  CAOSDB_TRANSACTION_STATUS_DEFAULT_FACTORY(EXECUTING, StatusCode::EXECUTING)
+  /**
+   * Factory for a SUCCESS status.
+   *
+   * This status means that the transaction has been executed successfully.
+   */
+  CAOSDB_TRANSACTION_STATUS_DEFAULT_FACTORY(SUCCESS, StatusCode::SUCCESS)
+  /**
+   * Factory for a CONNECTION_ERROR status.
+   *
+   * This status means that the connection could not be established. This is
+   * possibly due to misconfiguration of the client, errors in the network or
+   * because the server is down.
+   */
+  CAOSDB_TRANSACTION_STATUS_DEFAULT_FACTORY(CONNECTION_ERROR,
+                                            StatusCode::CONNECTION_ERROR)
+  /**
+   * Factory for an AUTHENTICATION_ERROR status.
+   *
+   * This status means that the RPC layer reported an authentication error.
+   */
+  CAOSDB_TRANSACTION_STATUS_DEFAULT_FACTORY(AUTHENTICATION_ERROR,
+                                            StatusCode::AUTHENTICATION_ERROR)
+  /**
+   * Another factory for an TRANSACTION_ERROR Status with a detailed
+   * description.
+   */
   inline static auto AUTHENTICATION_ERROR(const std::string &details)
     -> const TransactionStatus {
     return TransactionStatus(
@@ -86,19 +119,18 @@ public:
       caosdb::get_status_description(StatusCode::AUTHENTICATION_ERROR) +
         " Original error: " + details);
   }
-  inline static auto AUTHENTICATION_ERROR() -> const TransactionStatus & {
-    static const TransactionStatus authentication_error(
-      StatusCode::AUTHENTICATION_ERROR,
-      caosdb::get_status_description(StatusCode::AUTHENTICATION_ERROR));
-
-    return authentication_error;
-  }
-  inline static auto TRANSACTION_ERROR() -> const TransactionStatus & {
-    static const TransactionStatus transaction_error(
-      StatusCode::GENERIC_TRANSACTION_ERROR,
-      caosdb::get_status_description(StatusCode::GENERIC_TRANSACTION_ERROR));
-    return transaction_error;
-  }
+  /**
+   * Factory for a TRANSACTION_ERROR status.
+   *
+   * This status means that the transaction failed due to errors thrown by the
+   * server.
+   */
+  CAOSDB_TRANSACTION_STATUS_DEFAULT_FACTORY(
+    TRANSACTION_ERROR, StatusCode::GENERIC_TRANSACTION_ERROR)
+  /**
+   * Another factory for a TRANSACTION_ERROR status with a detailed
+   * description.
+   */
   inline static auto TRANSACTION_ERROR(const std::string &details)
     -> const TransactionStatus {
     return TransactionStatus(
@@ -106,23 +138,53 @@ public:
       caosdb::get_status_description(StatusCode::GENERIC_TRANSACTION_ERROR) +
         " Original error: " + details);
   }
+  /**
+   * Factory for a RPC_ERROR with a detailed description.
+   *
+   * This status is used for any error on the RPC layer.
+   */
+  inline static auto RPC_ERROR(const std::string &details)
+    -> const TransactionStatus {
+    // We use the GENERIC_RPC_ERROR here because we might want to add further
+    // RPC_ERROR states with different error codes (which stem from GRPC) here
+    // in the future.
+    return TransactionStatus(
+      StatusCode::GENERIC_RPC_ERROR,
+      caosdb::get_status_description(StatusCode::GENERIC_RPC_ERROR) +
+        " Original error: " + details);
+  }
 
   inline auto ThrowExceptionIfError() const -> void {
-    if (!IsError()) {
+    TransactionStatus::ThrowExceptionIfError(this->code, this->description);
+  }
+
+  inline static auto ThrowExceptionIfError(StatusCode code,
+                                           const std::string &description)
+    -> void {
+    if (!IsError(code)) {
       return;
     }
-    switch (this->code) {
+    switch (code) {
     case StatusCode::CONNECTION_ERROR:
-      throw ConnectionError(this->description);
+      throw ConnectionError(description);
     case StatusCode::AUTHENTICATION_ERROR:
-      throw AuthenticationError(this->description);
+      throw AuthenticationError(description);
     case StatusCode::GENERIC_TRANSACTION_ERROR:
-      throw TransactionError(this->description);
+      throw TransactionError(description);
+    case StatusCode::TRANSACTION_STATUS_ERROR:
+      throw TransactionStatusError(description);
+    case StatusCode::TRANSACTION_TYPE_ERROR:
+      throw TransactionTypeError(description);
     default:
-      throw Exception(StatusCode::GENERIC_ERROR, this->description);
+      throw Exception(StatusCode::GENERIC_ERROR, description);
     }
   }
 
+  /**
+   * Return true if the given StatusCode represents an erroneous state.
+   */
+  inline static auto IsError(StatusCode code) -> bool { return code > 0; };
+
   /**
    * Return true if this TransactionStatus represents a terminated state.
    */
@@ -131,7 +193,9 @@ public:
   /**
    * Return true if this TransactionStatus represents an erroneous state.
    */
-  inline auto IsError() const -> bool { return this->code > 0; };
+  inline auto IsError() const -> bool {
+    return TransactionStatus::IsError(this->code);
+  };
 
   /**
    * Return a description of the erroneous state.
diff --git a/src/caosdb/transaction.cpp b/src/caosdb/transaction.cpp
index d0b9025b7b1a5b48741e8687db9c21136864c683..f946175f3b33397e9398188500cf0ef51cc16d26 100644
--- a/src/caosdb/transaction.cpp
+++ b/src/caosdb/transaction.cpp
@@ -42,8 +42,14 @@ namespace caosdb {
 auto get_status_description(int code) -> const std::string & {
   static const std::string MISSING_DESCRIPTION = "MISSING DESCRIPTION";
   static const std::map<int, std::string> descriptions = {
-    {StatusCode::INITIAL, "The transaction has not been executed yet and "
-                          "clients can stil change it."},
+    {StatusCode::INITIAL,
+     "The transaction has just been intialized. It has not been executed yet "
+     "and clients can still change it and add sub-transactions."},
+    {StatusCode::GO_ON,
+     "The transaction has a transaction_type yet and clients may add matching "
+     "sub-transaction or execute it right-away."},
+    {StatusCode::READY,
+     "The transaction is ready for execution and cannot be changed anymore."},
     {StatusCode::EXECUTING, "The transaction is currently being executed."},
     {StatusCode::SUCCESS, "The action was successful"},
     {StatusCode::CONNECTION_ERROR,
@@ -68,6 +74,11 @@ auto get_status_description(int code) -> const std::string & {
     {StatusCode::TRANSACTION_TYPE_ERROR,
      "The Transaction has a transaction type which does not allow the "
      "attempted action."},
+    {StatusCode::ORIGINAL_ENTITY_MISSING_ID,
+     "The attempt to update this entity failed because this entity does not "
+     "have "
+     "an id. This is the case when you did not retrieve it before applying any "
+     "changes and instantiated the Entity class explicitely."},
     {StatusCode::UNSUPPORTED_FEATURE,
      "This feature is not available in the this client implementation."}};
   try {
@@ -90,6 +101,36 @@ using grpc::ClientAsyncResponseReader;
 using ProtoEntity = caosdb::entity::v1alpha1::Entity;
 using grpc::CompletionQueue;
 
+ResultSet::iterator::iterator(const ResultSet *result_set_param, int index)
+  : current_index(index), result_set(result_set_param) {}
+
+auto ResultSet::iterator::operator*() const -> const Entity & {
+  return this->result_set->At(current_index);
+}
+
+auto ResultSet::iterator::operator++() -> ResultSet::iterator & {
+  current_index++;
+  return *this;
+}
+
+auto ResultSet::iterator::operator++(int) -> ResultSet::iterator {
+  iterator tmp(*this);
+  operator++();
+  return tmp;
+}
+
+auto ResultSet::iterator::operator!=(const iterator &rhs) const -> bool {
+  return this->current_index != rhs.current_index;
+}
+
+auto ResultSet::begin() const -> ResultSet::iterator {
+  return ResultSet::iterator(this, 0);
+}
+
+auto ResultSet::end() const -> ResultSet::iterator {
+  return ResultSet::iterator(this, Size());
+}
+
 MultiResultSet::MultiResultSet(MultiTransactionResponse *response) {
   auto *responses = response->mutable_responses();
   for (auto sub_response : *responses) {
@@ -133,16 +174,21 @@ auto Transaction::RetrieveById(const std::string &id) noexcept -> StatusCode {
   auto *sub_request = this->request->add_requests();
   sub_request->mutable_retrieve_request()->set_id(id);
 
-  return StatusCode::INITIAL;
+  this->status = TransactionStatus::GO_ON();
+  return this->status.GetCode();
 }
 
 auto Transaction::Query(const std::string &query) noexcept -> StatusCode {
-  ASSERT_CAN_ADD_RETRIEVAL
+  ASSERT_CAN_ADD_QUERY
 
+  // currently, the server can only process one query at a time (but mixed with
+  // other retrievals).
+  this->has_query = true;
   auto *sub_request = this->request->add_requests();
   sub_request->mutable_retrieve_request()->mutable_query()->set_query(query);
 
-  return StatusCode::INITIAL;
+  this->status = TransactionStatus::GO_ON();
+  return this->status.GetCode();
 }
 
 auto Transaction::DeleteById(const std::string &id) noexcept -> StatusCode {
@@ -151,7 +197,8 @@ auto Transaction::DeleteById(const std::string &id) noexcept -> StatusCode {
   auto *sub_request = this->request->add_requests();
   sub_request->mutable_delete_request()->set_id(id);
 
-  return StatusCode::INITIAL;
+  this->status = TransactionStatus::READY();
+  return this->status.GetCode();
 }
 
 auto Transaction::InsertEntity(Entity *entity) noexcept -> StatusCode {
@@ -162,18 +209,33 @@ auto Transaction::InsertEntity(Entity *entity) noexcept -> StatusCode {
 
   // copy the original entity for the transaction
   entity->CopyTo(proto_entity);
-  return StatusCode::INITIAL;
+  this->status = TransactionStatus::READY();
+  return this->status.GetCode();
+}
+
+auto Transaction::UpdateEntity(Entity *entity) noexcept -> StatusCode {
+  ASSERT_CAN_ADD_UPDATE
+
+  auto *sub_request = this->request->add_requests();
+  auto *proto_entity = sub_request->mutable_update_request();
+
+  entity->CopyTo(proto_entity);
+  this->status = TransactionStatus::READY();
+  return this->status.GetCode();
 }
 
 auto Transaction::Execute() -> TransactionStatus {
-  ExecuteAsynchronously();
+  auto status_code = ExecuteAsynchronously();
+  TransactionStatus::ThrowExceptionIfError(
+    status_code, caosdb::get_status_description(status_code));
   auto status = WaitForIt();
   status.ThrowExceptionIfError();
   return status;
 }
 
 auto Transaction::ExecuteAsynchronously() noexcept -> StatusCode {
-  if (!IsStatus(TransactionStatus::INITIAL())) {
+  if (!IsStatus(TransactionStatus::READY()) &&
+      !IsStatus(TransactionStatus::GO_ON())) {
     return StatusCode::TRANSACTION_STATUS_ERROR;
   }
   switch (this->transaction_type) {
@@ -245,6 +307,14 @@ auto Transaction::WaitForIt() const noexcept -> TransactionStatus {
       }
       this->result_set = std::make_unique<UniqueResult>(entity);
     } break;
+    case WrappedResponseCase::kUpdateResponse: {
+      auto *updatedIdResponse = responses->mutable_update_response();
+      if (!updatedIdResponse->entity_errors().empty()) {
+        this->status = TransactionStatus::TRANSACTION_ERROR(
+          "The request returned with errors.");
+      }
+      this->result_set = std::make_unique<UniqueResult>(updatedIdResponse);
+    } break;
     case WrappedResponseCase::kInsertResponse: {
       auto *insertedIdResponse = responses->mutable_insert_response();
       if (!insertedIdResponse->entity_errors().empty()) {
diff --git a/test/test_entity.cpp b/test/test_entity.cpp
index 6a24ab7cb11ae59e6a0cbffed499286c773a399f..3639f8c574ae90e70f0ef9abeb77a4403fadc06b 100644
--- a/test/test_entity.cpp
+++ b/test/test_entity.cpp
@@ -30,9 +30,7 @@
 #include "gtest/gtest-message.h"                 // for Message
 #include "gtest/gtest-test-part.h"               // for TestPartResult, Sui...
 #include "gtest/gtest_pred_impl.h"               // for Test, EXPECT_EQ
-#include <iostream>                              // for endl, basic_ostream
 #include <memory>                                // for allocator, shared_ptr
-#include <string>                                // for operator<<
 
 namespace caosdb::entity {
 using caosdb::entity::v1alpha1::IdResponse;
@@ -105,7 +103,7 @@ TEST(test_entity, test_append_property) {
 
 TEST(test_entity, test_copy_to) {
   auto entity = Entity();
-  entity.SetId("original_id");
+  entity.SetRole("original_role");
   entity.SetName("orignial_name");
 
   auto parent = Parent();
@@ -125,7 +123,7 @@ TEST(test_entity, test_copy_to) {
   entity.CopyTo(proto_copy);
   auto copied = Entity(proto_copy);
 
-  EXPECT_EQ(entity.GetId(), copied.GetId());
+  EXPECT_EQ(entity.GetRole(), copied.GetRole());
   EXPECT_EQ(entity.GetName(), copied.GetName());
   EXPECT_EQ(copied.GetParents().At(0).GetId(), parent.GetId());
   EXPECT_EQ(copied.GetParents().At(0).GetName(), parent.GetName());
@@ -138,16 +136,16 @@ TEST(test_entity, test_insert_entity) {
     std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr));
 
   auto entity = Entity();
-  entity.SetId("entity_id");
-  entity.SetVersionId("version_id");
+  entity.SetRole("entity_role");
+  entity.SetName("entity_name");
 
-  EXPECT_EQ(entity.GetId(), "entity_id");
-  EXPECT_EQ(entity.GetVersionId(), "version_id");
+  EXPECT_EQ(entity.GetRole(), "entity_role");
+  EXPECT_EQ(entity.GetName(), "entity_name");
 
   transaction.InsertEntity(&entity);
 
-  EXPECT_EQ(entity.GetId(), "entity_id");
-  EXPECT_EQ(entity.GetVersionId(), "version_id");
+  EXPECT_EQ(entity.GetRole(), "entity_role");
+  EXPECT_EQ(entity.GetName(), "entity_name");
 }
 
 TEST(test_entity, test_insert_with_role) {
@@ -155,8 +153,6 @@ TEST(test_entity, test_insert_with_role) {
     std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr));
 
   auto entity = Entity();
-  entity.SetId("entity_id");
-  entity.SetVersionId("version_id");
   entity.SetRole("Property");
   entity.SetDatatype("DOUBLE");
   entity.SetName("Length");
@@ -165,8 +161,6 @@ TEST(test_entity, test_insert_with_role) {
 
   transaction.InsertEntity(&entity);
 
-  EXPECT_EQ(entity.GetId(), "entity_id");
-  EXPECT_EQ(entity.GetVersionId(), "version_id");
   EXPECT_EQ(entity.GetRole(), "Property");
   EXPECT_EQ(entity.GetDatatype(), "DOUBLE");
   EXPECT_EQ(entity.GetName(), "Length");
@@ -179,7 +173,7 @@ TEST(test_entity, test_insert_with_parent) {
     std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr));
 
   auto entity = Entity();
-  entity.SetId("entity_id");
+  entity.SetName("entity_name");
 
   auto parent = Parent();
   parent.SetId("parent_id");
@@ -191,8 +185,7 @@ TEST(test_entity, test_insert_with_parent) {
 
   transaction.InsertEntity(&entity);
 
-  std::cout << entity.ToString() << std::endl;
-  EXPECT_EQ(entity.GetId(), "entity_id");
+  EXPECT_EQ(entity.GetName(), "entity_name");
   EXPECT_EQ(entity.GetParents().Size(), 1);
   auto inserted_parent = entity.GetParents().At(0);
   EXPECT_EQ(inserted_parent.GetId(), parent.GetId());
@@ -204,7 +197,7 @@ TEST(test_entity, test_insert_with_property) {
     std::shared_ptr<transaction::EntityTransactionService::Stub>(nullptr));
 
   auto entity = Entity();
-  entity.SetId("entity_id");
+  entity.SetName("entity_name");
 
   auto prop = Property();
   prop.SetName("prop_name");
@@ -239,7 +232,6 @@ TEST(test_entity, test_from_id_response) {
 
   Entity entity(&idResponse);
 
-  std::cout << entity.ToString() << std::endl;
   EXPECT_EQ(entity.GetId(), "entity_id");
   EXPECT_TRUE(entity.HasErrors());
   EXPECT_EQ(entity.GetErrors().Size(), 1);
@@ -268,5 +260,4 @@ TEST(test_entity, test_from_id_response) {
   EXPECT_EQ(other_ent.GetInfos().At(0).GetDescription(), "info_desc");
   EXPECT_EQ(other_ent.GetInfos().At(0).GetCode(), MessageCode::UNSPECIFIED);
 }
-
 } // namespace caosdb::entity
diff --git a/test/test_transaction.cpp b/test/test_transaction.cpp
index 118208595ea5d100dacb8302be555772c8fcd80e..557ded7b87fe9947391bb47f6c37b49ee1824cfb 100644
--- a/test/test_transaction.cpp
+++ b/test/test_transaction.cpp
@@ -29,10 +29,9 @@
 #include "gtest/gtest-message.h"       // for Message
 #include "gtest/gtest-test-part.h"     // for SuiteApiResolver, TestPa...
 #include "gtest/gtest_pred_impl.h"     // for Test, TestInfo, TEST
-#include <iostream>
-#include <memory> // for allocator, unique_ptr
-#include <string> // for string, basic_string
-#include <vector> // for vector
+#include <memory>                      // for allocator, unique_ptr
+#include <string>                      // for string, basic_string
+#include <vector>                      // for vector
 
 namespace caosdb::transaction {
 using caosdb::configuration::InsecureConnectionConfiguration;
@@ -47,7 +46,7 @@ TEST(test_transaction, create_transaction) {
   Connection connection(configuration);
   auto transaction = connection.CreateTransaction();
 
-  transaction->RetrieveById("100");
+  ASSERT_EQ(StatusCode::GO_ON, transaction->RetrieveById("100"));
   EXPECT_THROW_MESSAGE(
     transaction->Execute(), ConnectionError,
     "The attempt to execute this transaction was not successful because the "
@@ -99,6 +98,33 @@ TEST(test_transaction, test_multi_result_set_empty) {
   EXPECT_EQ(rs.Size(), 0);
 }
 
+TEST(test_transaction, test_multi_result_iterator) {
+  MultiTransactionResponse response;
+
+  response.add_responses()
+    ->mutable_retrieve_response()
+    ->mutable_entity()
+    ->set_id("100");
+
+  MultiResultSet rs(&response);
+  EXPECT_EQ(rs.Size(), 1);
+
+  for (const Entity &entity : rs) {
+    EXPECT_EQ(entity.GetId(), "100");
+  }
+}
+
+TEST(test_transaction, test_unique_result_iterator) {
+  caosdb::entity::v1alpha1::Entity response;
+  response.set_id("100");
+
+  UniqueResult rs(&response);
+  EXPECT_EQ(rs.Size(), 1);
+  for (const Entity &entity : rs) {
+    EXPECT_EQ(entity.GetId(), "100");
+  }
+}
+
 TEST(test_transaction, test_multi_result_set_one) {
   MultiTransactionResponse response;
   response.add_responses()
@@ -129,7 +155,19 @@ TEST(test_transaction, test_multi_result_set_three) {
   MultiResultSet rs(&response);
   EXPECT_EQ(rs.Size(), 3);
   EXPECT_TRUE(rs.At(1).HasErrors());
-  std::cout << rs.At(1).ToString();
+}
+
+TEST(test_transaction, test_update_entity) {
+  const auto *host = "localhost";
+  auto configuration = InsecureConnectionConfiguration(host, 8000);
+  Connection connection(configuration);
+  auto transaction = connection.CreateTransaction();
+
+  caosdb::entity::Entity update_entity;
+  update_entity.SetRole("New role");
+  auto error = transaction->UpdateEntity(&update_entity);
+
+  EXPECT_EQ(error, StatusCode::ORIGINAL_ENTITY_MISSING_ID);
 }
 
 } // namespace caosdb::transaction