Value-Preserving Masking
Overview
This guide describes how to create a mapping table for value-preserving masking in XDM.
The goal is to shuffle values, so that sensitive data is masked while valid values from the original dataset are preserved.
Identical source values are mapped to identical target values. This ensures consistent masking results when the same value appears multiple times or is used across different tables.
Prerequisites
This guide uses the employees table from the tutorial’s sample environment.
Before using this approach, ensure that the following prerequisites are met:
-
A connection to the database where the source table is located. In this example, a connection to the sample environment is required.
-
Read access to the source table.
-
A target mapping table where the generated key/value pairs can be stored.
-
Write access to the target mapping table.
| The target can be an empty H2 mapping table in XDM. It does not need to be located in a Mapping Table Container. |
| It is also possible to use an existing database table or an existing mapping table as the basis for shuffling. |
Steps
| All steps described below must be executed in one hook. |
Step 1: Prepare the Workspace
Import the required package and initialize the helper classes.
import de.ubs.xdm3.batch.modification.MappingTableUtils
def utilPOOL = new MappingTableUtils(myConnection)
def poolTable = 'TABLE_2'
In this example:
-
myConnectionis the connection to the target mapping table. -
TABLE_2is the target mapping table where the shuffled key/value pairs are stored.
Step 2: Read All Relevant Values from the Source Table
Retrieve the values that should be shuffled.
In this example, the distinct employee last names are read from the production.employees table.
def stmt = myConnection.createStatement()
def rs = stmt.executeQuery("SELECT DISTINCT last_name FROM production.employees")
def valueList = []
while (rs.next()) {
valueList << rs.getString(1)
}
rs.close()
stmt.close()
The DISTINCT clause ensures that each last name is included only once.
This creates one unique assignment per name.
If the frequency of values should be taken into account, extract the list of IDs in addition to the list of names.
Example:
def stmt = myConnection.createStatement()
def rs1 = stmt.executeQuery("SELECT emp_no FROM production.employees")
def valueList1 = []
while (rs1.next()) {
valueList1 << rs1.getString(1)
}
rs1.close()
def rs2 = stmt.executeQuery("SELECT last_name FROM production.employees")
def valueList2 = []
while (rs2.next()) {
valueList2 << rs2.getString(1)
}
rs2.close()
stmt.close()
| This process keeps the full list in memory. Therefore, it has limitations depending on the available memory. It is not suitable for very large tables, for example tables with more than 100 million entries. |
Step 3: Create and Shuffle Two Lists
Create two separate copies of the value list and shuffle both.
def list1 = new ArrayList(valueList)
def list2 = new ArrayList(valueList)
Collections.shuffle(list1)
Collections.shuffle(list2)
This creates a random but reproducible mapping table after the values have been stored.
If two lists are selected from the database, use:
-
valueList1forlist1 -
valueList2forlist2
Example:
def list1 = new ArrayList(valueList1)
def list2 = new ArrayList(valueList2)
Collections.shuffle(list1)
Collections.shuffle(list2)
Step 4: Generate and Populate the Mapping Table
Pair the entries from both lists and store the key/value pairs in the mapping table.
def n = Math.min(list1.size(), list2.size())
for (int i = 0; i < n; i++) {
def key = [list1[i]] as String[]
def data = [list2[i]] as String[]
utilPOOL.merge(poolTable, key, data)
println "Pair: ${key[0]} -> ${data[0]}"
}
utilPOOL.close()
The generated mapping table can then be used in a Modification Method to replace original values with their mapped target values.
Benefits
This approach provides the following benefits:
-
Consistent mapping: Identical source values are always mapped to the same target value.
-
Realistic test data: Masked values still look valid because they come from the original data domain.
-
Automation: The mapping table can be generated automatically and reused in masking processes.