private void deleteTextContent(final long storedEmailId) {
    QTextContent qTextContent = QTextContent.textContent;
    Tuple tuple =
        querydslSupport.execute(
            (connection, configuration) -> {
              return new SQLQuery(connection, configuration)
                  .from(qTextContent)
                  .where(qTextContent.storedEmailId.eq(storedEmailId))
                  .uniqueResult(qTextContent.textContentId, qTextContent.blobId);
            });

    if (tuple == null) {
      return;
    }

    Long textContentId = tuple.get(qTextContent.textContentId);
    querydslSupport.execute(
        (connection, configuration) -> {
          return new SQLDeleteClause(connection, configuration, qTextContent)
              .where(qTextContent.textContentId.eq(textContentId))
              .execute();
        });

    deleteBlob(tuple.get(qTextContent.blobId));
  }
  private Collection<Attachment> readAttachments(final long storedEmailId) {
    QAttachment qAttachment = QAttachment.attachment;
    QBinaryContent qBinaryContent = QBinaryContent.binaryContent;
    List<Tuple> attachmentTuples =
        querydslSupport.execute(
            (connection, configuration) -> {
              return new SQLQuery(connection, configuration)
                  .from(qAttachment)
                  .join(qBinaryContent)
                  .on(qAttachment.binaryContentId.eq(qBinaryContent.binaryContentId))
                  .where(qAttachment.storedEmailId.eq(storedEmailId))
                  .orderBy(qAttachment.index_.asc())
                  .list(qBinaryContent.blobId, qBinaryContent.contentType_, qBinaryContent.name_);
            });

    Collection<Attachment> attachments = new ArrayList<>();
    for (Tuple attachmentTuple : attachmentTuples) {
      String contentType = attachmentTuple.get(qBinaryContent.contentType_);
      String name = attachmentTuple.get(qBinaryContent.name_);
      Long blobId = attachmentTuple.get(qBinaryContent.blobId);
      Attachment attachment = createAttachment(blobId, contentType, name);

      attachments.add(attachment);
    }
    return attachments;
  }
  private HtmlContent readHtmlContent(final long storedEmailId) {
    QHtmlContent qHtmlContent = QHtmlContent.htmlContent;
    Tuple htmlContentTuple =
        querydslSupport.execute(
            (connection, configuration) -> {
              return new SQLQuery(connection, configuration)
                  .from(qHtmlContent)
                  .where(qHtmlContent.storedEmailId.eq(storedEmailId))
                  .uniqueResult(qHtmlContent.blobId, qHtmlContent.htmlContentId);
            });

    if (htmlContentTuple == null) {
      return null;
    }

    HtmlContent htmlContent = new HtmlContent();
    byte[] htmlContentBytes = readBlob(htmlContentTuple.get(qHtmlContent.blobId));
    if (htmlContentBytes.length > 0) {
      htmlContent.withHtml(new String(htmlContentBytes, StandardCharsets.UTF_8));
    }
    Map<String, Attachment> inlineImageByCidMap =
        readInlineImages(htmlContentTuple.get(qHtmlContent.htmlContentId));
    if (!inlineImageByCidMap.isEmpty()) {
      htmlContent.withInlineImageByCidMap(inlineImageByCidMap);
    }
    return htmlContent;
  }
  private Map<String, Attachment> readInlineImages(final long htmlContentId) {
    QBinaryContent qBinaryContent = QBinaryContent.binaryContent;
    QInlineImage qInlineImage = QInlineImage.inlineImage;
    List<Tuple> inlineImageTuples =
        querydslSupport.execute(
            (connection, configuration) -> {
              return new SQLQuery(connection, configuration)
                  .from(qInlineImage)
                  .leftJoin(qBinaryContent)
                  .on(qBinaryContent.binaryContentId.eq(qInlineImage.binaryContentId))
                  .where(qInlineImage.htmlContentId.eq(htmlContentId))
                  .orderBy(qInlineImage.index_.asc())
                  .list(
                      qBinaryContent.blobId,
                      qBinaryContent.contentType_,
                      qBinaryContent.name_,
                      qInlineImage.cid_);
            });

    Map<String, Attachment> inlineImageByCidMap = new LinkedHashMap<>();
    for (Tuple inlineImageTuple : inlineImageTuples) {
      String contentType = inlineImageTuple.get(qBinaryContent.contentType_);
      String name = inlineImageTuple.get(qBinaryContent.name_);
      Long blobId = inlineImageTuple.get(qBinaryContent.blobId);
      Attachment attachment = createAttachment(blobId, contentType, name);

      inlineImageByCidMap.put(inlineImageTuple.get(qInlineImage.cid_), attachment);
    }
    return inlineImageByCidMap;
  }
 private void deleteAllSimpleSubjects() {
   querydslSupport.execute(
       (connection, configuration) -> {
         QSimpleSubject qSimpleSubject = QSimpleSubject.simpleSubject;
         new SQLDeleteClause(connection, configuration, qSimpleSubject).execute();
         return null;
       });
 }
 private void deleteEmailAddresses(final List<Long> emailAddressIds) {
   querydslSupport.execute(
       (connection, configuration) -> {
         QAddress qAddress = QAddress.address1;
         return new SQLDeleteClause(connection, configuration, qAddress)
             .where(qAddress.emailAddressId.in(emailAddressIds))
             .execute();
       });
 }
 private void deleteEmail(final long storedEmailId) {
   querydslSupport.execute(
       (connection, configuration) -> {
         QEmail qEmail = QEmail.email;
         return new SQLDeleteClause(connection, configuration, qEmail)
             .where(qEmail.storedEmailId.eq(storedEmailId))
             .execute();
       });
 }
 private long insertEmail(final String subject) {
   return querydslSupport.execute(
       (connection, configuration) -> {
         QEmail qEmail = QEmail.email;
         return new SQLInsertClause(connection, configuration, qEmail)
             .set(qEmail.subject_, subject)
             .executeWithKey(qEmail.storedEmailId);
       });
 }
 private long insertAddress(final EmailAddress emailAddress) {
   return querydslSupport.execute(
       (connection, configuration) -> {
         QAddress qAddress = QAddress.address1;
         return new SQLInsertClause(connection, configuration, qAddress)
             .set(qAddress.address, emailAddress.address)
             .set(qAddress.personal, emailAddress.personal)
             .executeWithKey(qAddress.emailAddressId);
       });
 }
  private void deleteInlineImages(final long htmlContentId) {
    QInlineImage qInlineImage = QInlineImage.inlineImage;
    List<Long> binaryContentIds =
        querydslSupport.execute(
            (connection, configuration) -> {
              return new SQLQuery(connection, configuration)
                  .from(qInlineImage)
                  .where(qInlineImage.htmlContentId.eq(htmlContentId))
                  .list(qInlineImage.binaryContentId);
            });

    querydslSupport.execute(
        (connection, configuration) -> {
          return new SQLDeleteClause(connection, configuration, qInlineImage)
              .where(qInlineImage.htmlContentId.eq(htmlContentId))
              .execute();
        });

    deleteBinaryContents(binaryContentIds);
  }
  private void deleteAttachments(final long storedEmailId) {
    QAttachment qAttachment = QAttachment.attachment;
    List<Long> binaryContentIds =
        querydslSupport.execute(
            (connection, configuration) -> {
              return new SQLQuery(connection, configuration)
                  .from(qAttachment)
                  .where(qAttachment.storedEmailId.eq(storedEmailId))
                  .list(qAttachment.binaryContentId);
            });

    querydslSupport.execute(
        (connection, configuration) -> {
          return new SQLDeleteClause(connection, configuration, qAttachment)
              .where(qAttachment.storedEmailId.eq(storedEmailId))
              .execute();
        });

    deleteBinaryContents(binaryContentIds);
  }
  private void deleteRecipients(final long storedEmailId) {
    QEmailRecipient qEmailRecipient = QEmailRecipient.emailRecipient;
    List<Long> emailAddressIds =
        querydslSupport.execute(
            (connection, configuration) -> {
              return new SQLQuery(connection, configuration)
                  .from(qEmailRecipient)
                  .where(qEmailRecipient.storedEmailId.eq(storedEmailId))
                  .list(qEmailRecipient.emailAddressId);
            });

    if (emailAddressIds.isEmpty()) {
      return;
    }

    querydslSupport.execute(
        (connection, configuration) -> {
          return new SQLDeleteClause(connection, configuration, qEmailRecipient)
              .where(qEmailRecipient.storedEmailId.eq(storedEmailId))
              .execute();
        });

    deleteEmailAddresses(emailAddressIds);
  }
  private void insertTextContent(final String textContent, final long storedEmailId) {
    if (textContent == null) {
      return;
    }

    Long textContentBlobId = createBlob(getBytes(textContent));
    querydslSupport.execute(
        (connection, configuration) -> {
          QTextContent qTextContent = QTextContent.textContent;
          return new SQLInsertClause(connection, configuration, qTextContent)
              .set(qTextContent.storedEmailId, storedEmailId)
              .set(qTextContent.blobId, textContentBlobId)
              .executeWithKey(qTextContent.textContentId);
        });
  }
  private void deleteBinaryContents(final List<Long> binaryContentIds) {
    if (binaryContentIds.isEmpty()) {
      return;
    }

    QBinaryContent qBinaryContent = QBinaryContent.binaryContent;
    List<Long> blobIds =
        querydslSupport.execute(
            (connection, configuration) -> {
              return new SQLQuery(connection, configuration)
                  .from(qBinaryContent)
                  .where(qBinaryContent.binaryContentId.in(binaryContentIds))
                  .list(qBinaryContent.blobId);
            });

    querydslSupport.execute(
        (connection, configuration) -> {
          return new SQLDeleteClause(connection, configuration, qBinaryContent)
              .where(qBinaryContent.binaryContentId.in(binaryContentIds))
              .execute();
        });

    deleteBlobs(blobIds);
  }
 private String readTextContent(final long storedEmailId) {
   Long textContentBlobId =
       querydslSupport.execute(
           (connection, configuration) -> {
             QTextContent qTextContent = QTextContent.textContent;
             return new SQLQuery(connection, configuration)
                 .from(qTextContent)
                 .where(qTextContent.storedEmailId.eq(storedEmailId))
                 .uniqueResult(qTextContent.blobId);
           });
   if (textContentBlobId != null) {
     byte[] textContentBytes = readBlob(textContentBlobId);
     return new String(textContentBytes, StandardCharsets.UTF_8);
   }
   return null;
 }
 private void lockEmailForUpdate(final long storedEmailId) {
   Boolean exists =
       querydslSupport.execute(
           (connection, configuration) -> {
             QEmail qEmail = QEmail.email;
             return new SQLQuery(connection, configuration)
                 .from(qEmail)
                 .where(qEmail.storedEmailId.eq(storedEmailId))
                 .forUpdate()
                 .exists();
           });
   if (!exists) {
     throw new NonExistentEmailException(
         "Stored email not exists [storeEmailId: " + storedEmailId + "]");
   }
 }
  private void insertHtmlContent(final HtmlContent htmlContent, final long storedEmailId) {
    if (htmlContent == null) {
      return;
    }

    Long htmlContentBlobId = createBlob(getBytes(htmlContent.html));
    Long htmlContentId =
        querydslSupport.execute(
            (connection, configuration) -> {
              QHtmlContent qHtmlContent = QHtmlContent.htmlContent;
              return new SQLInsertClause(connection, configuration, qHtmlContent)
                  .set(qHtmlContent.blobId, htmlContentBlobId)
                  .set(qHtmlContent.storedEmailId, storedEmailId)
                  .executeWithKey(qHtmlContent.htmlContentId);
            });
    insertInlineImages(htmlContent.inlineImageByCidMap, htmlContentId);
  }
  private Long insertBinaryContent(final Attachment attachment) {
    Objects.requireNonNull(attachment, "attachment cannot be null");

    return querydslSupport.execute(
        (connection, configuration) -> {
          Long attachmentBlobId = null;
          if (attachment.inputStreamSupplier != null) {
            attachmentBlobId = createBlob(attachment.inputStreamSupplier.getStream());
          }

          QBinaryContent qBinaryContent = QBinaryContent.binaryContent;
          return new SQLInsertClause(connection, configuration, qBinaryContent)
              .set(qBinaryContent.name_, attachment.name)
              .set(qBinaryContent.contentType_, attachment.contentType)
              .set(qBinaryContent.blobId, attachmentBlobId)
              .executeWithKey(qBinaryContent.binaryContentId);
        });
  }
  private void insertRecipient(
      final EmailAddress emailAddress,
      final RecipientType recipientType,
      final int index,
      final long storedEmailId) {
    long emailAddressId = insertAddress(emailAddress);

    querydslSupport.execute(
        (connection, configuration) -> {
          QEmailRecipient qEmailRecipient = QEmailRecipient.emailRecipient;
          return new SQLInsertClause(connection, configuration, qEmailRecipient)
              .set(qEmailRecipient.emailAddressId, emailAddressId)
              .set(qEmailRecipient.index_, index)
              .set(qEmailRecipient.storedEmailId, storedEmailId)
              .set(qEmailRecipient.recipientType, recipientType.name())
              .executeWithKey(qEmailRecipient.emailRecipientId);
        });
  }
  private void insertAttachments(
      final Collection<Attachment> attachments, final long storedEmailId) {
    Objects.requireNonNull(attachments, "attachments collection cannot be null");

    querydslSupport.execute(
        (connection, configuration) -> {
          int index = EMAIL_CONTENT_START_INDEX;
          for (Attachment attachment : attachments) {
            Long binaryContentId = insertBinaryContent(attachment);
            QAttachment qAttachment = QAttachment.attachment;
            new SQLInsertClause(connection, configuration, qAttachment)
                .set(qAttachment.binaryContentId, binaryContentId)
                .set(qAttachment.index_, index++)
                .set(qAttachment.storedEmailId, storedEmailId)
                .executeWithKey(qAttachment.attachmentId);
          }
          return null;
        });
  }
 private List<EmailAddress> readEmailAddress(
     final long storedEmailId, final RecipientType recipientType) {
   return querydslSupport.execute(
       (connection, configuration) -> {
         QAddress qAddress = QAddress.address1;
         QEmailRecipient qEmailRecipient = QEmailRecipient.emailRecipient;
         return new SQLQuery(connection, configuration)
             .from(qAddress)
             .join(qEmailRecipient)
             .on(qEmailRecipient.emailAddressId.eq(qAddress.emailAddressId))
             .where(
                 qEmailRecipient
                     .storedEmailId
                     .eq(storedEmailId)
                     .and(qEmailRecipient.recipientType.eq(recipientType.name())))
             .orderBy(qEmailRecipient.index_.asc())
             .list(Projections.fields(EmailAddress.class, qAddress.personal, qAddress.address));
       });
 }
  private void insertInlineImages(
      final Map<String, Attachment> inlineImageByCidMap, final long htmlContentId) {
    Objects.requireNonNull(inlineImageByCidMap, "inlineImageByCidMap cannot be null");

    querydslSupport.execute(
        (connection, configuration) -> {
          int index = EMAIL_CONTENT_START_INDEX;
          for (Map.Entry<String, Attachment> entry : inlineImageByCidMap.entrySet()) {
            Long binaryContentId = insertBinaryContent(entry.getValue());
            QInlineImage qInlineImage = QInlineImage.inlineImage;
            new SQLInsertClause(connection, configuration, qInlineImage)
                .set(qInlineImage.cid_, entry.getKey())
                .set(qInlineImage.binaryContentId, binaryContentId)
                .set(qInlineImage.index_, index++)
                .set(qInlineImage.htmlContentId, htmlContentId)
                .executeWithKey(qInlineImage.inlineImageId);
          }
          return null;
        });
  }
  @Override
  public Email read(final long storedEmailId) {
    return transactionPropagator.required(
        () -> {
          QEmail qEmail = QEmail.email;
          Tuple emailTuple =
              querydslSupport.execute(
                  (connection, configuration) -> {
                    return new SQLQuery(connection, configuration)
                        .from(qEmail)
                        .where(qEmail.storedEmailId.eq(storedEmailId))
                        .uniqueResult(qEmail.subject_, qEmail.storedEmailId);
                  });

          if (emailTuple == null) {
            return null;
          }

          Email email = new Email().withSubject(emailTuple.get(qEmail.subject_));

          email.withTextContent(readTextContent(storedEmailId));

          HtmlContent htmlContent = readHtmlContent(storedEmailId);
          email = email.withHtmlContent(htmlContent);

          Collection<Attachment> attachments = readAttachments(storedEmailId);
          email = email.withAttachments(attachments);

          EmailAddress from = readFrom(storedEmailId);
          email = email.withFrom(from);

          Recipients recipients = readRecipients(storedEmailId);
          email.withRecipients(recipients);

          return email;
        });
  }