Other data storage
You can use the GDPR for Akka Persistence encryption utilities even if you are not using Akka Persistence or Lagom Persistence. However, first consider whether it’s easier to perform ordinary removal of the data instead of encrypting and shredding it.
Here is an example of how to use the WithDataSubjectIdSerialization
utility in a JDBC data access object. The same technique can be used with Lagom’s Read-side Lagom’s Read-side, plain JPA, a NoSQL key value store, or many other data stores.
Each WithDataSubjectId
part must be represented as bytes (blob) in the stored representation and you should use the WithDataSubjectIdSerialization
utility to serialize each WithDataSubjectId
to/from the bytes.
- Scala
-
final case class PersonalInformation(name: String, email: String) final case class Customer(id: Long, personal: WithDataSubjectId[PersonalInformation]) class CustomerDbAccess(system: ActorSystem, blockingExectionContext: ExecutionContext) { private val withDataSubjectIdSerialization = new WithDataSubjectIdSerialization(system) // important to use a dedicated dispatcher for the blocking JDBC calls private implicit val ec: ExecutionContext = blockingExectionContext private val updatePstmt: PreparedStatement = ??? // TODO the prepared statement for the update private val readPstmt: PreparedStatement = ??? // TODO the prepared statement for the read def update(customer: Customer): Future[Done] = { withDataSubjectIdSerialization.toBinaryAsync(customer.personal).map { personalBytes => updatePstmt.setLong(1, customer.id) updatePstmt.setBlob(2, new SerialBlob(personalBytes)) updatePstmt.execute() Done } } def read(customerId: Long): Future[Customer] = { def personalBytes(): Future[Array[Byte]] = Future { val rs = readPstmt.executeQuery() try { val blob = rs.getBlob(2) blob.getBytes(0, blob.length.toInt) } finally { rs.close() } } for { bytes <- personalBytes() personal <- withDataSubjectIdSerialization.fromBinaryAsync[PersonalInformation](bytes) } yield { Customer(customerId, personal) } } }
- Java
-
public static class PersonalInformation { public final String name; public final String email; public PersonalInformation(String name, String email) { this.name = name; this.email = email; } } public static class Customer { public final long id; public final WithDataSubjectId<PersonalInformation> personal; public Customer(long id, WithDataSubjectId<PersonalInformation> personal) { this.id = id; this.personal = personal; } } public static class CustomerDbAccess { private final ActorSystem system; // important to use a dedicated thread pool for the blocking JDBC calls private final Executor blockingExecutor; private final WithDataSubjectIdSerialization withDataSubjectIdSerialization; private PreparedStatement updatePstmt = null; // TODO the prepared statement for the update private PreparedStatement readPstmt = null; // TODO the prepared statement for the read public CustomerDbAccess(ActorSystem system, Executor blockingExecutor) { this.system = system; this.blockingExecutor = blockingExecutor; this.withDataSubjectIdSerialization = new WithDataSubjectIdSerialization(system); } public CompletionStage<Done> update(Customer customer) { return withDataSubjectIdSerialization.toBinaryAsync(customer.personal) .thenApplyAsync(personalBytes -> { try { updatePstmt.setLong(1, customer.id); updatePstmt.setBlob(2, new SerialBlob(personalBytes)); updatePstmt.execute(); return Done.getInstance(); } catch (SQLException e) { throw new RuntimeException(e); } }, blockingExecutor); } public CompletionStage<Customer> read(long customerId) { CompletionStage<byte[]> personalBytes = CompletableFuture.supplyAsync(() -> { ResultSet rs = null; try { rs = readPstmt.executeQuery(); Blob blob = rs.getBlob(2); return blob.getBytes(0, (int) blob.length()); } catch (SQLException e) { throw new RuntimeException(e); } finally { if (rs != null) { try { rs.close(); } catch (SQLException ignore) { } } } }, blockingExecutor); CompletionStage<Customer> customer = personalBytes.thenComposeAsync(bytes -> { return withDataSubjectIdSerialization.fromBinaryAsync(PersonalInformation.class, bytes) .thenApply(personal -> { return new Customer(customerId, personal); }); }, blockingExecutor); return customer; } }