-
Notifications
You must be signed in to change notification settings - Fork 2
Setting up an Entity Persistence
We shall implement an Ad entity starting with a single table with few fields.
Add this &rewriteBatchedStatements=true
to the connection string or else the JDBC driver will split our bulks to separate commands and the performance penalty for that is amazingly high.
You don’t need to repeat it for all entities but it is rather a one-time setup.
PLContext is a wrapper around DSLContext of JOOQ. This is where you tell JOOQ about your DB DataSource.
public DSLContext defineJooq() {
DSLContext jooq = DSL.using(new DefaultConfiguration()
.set(SQLDialect.MYSQL)
.set(new ThreadLocalTransactionProvider(/* and your JDBC connection provider */)));
return new PLContext.Builder(jooq).build();
}
Define a JOOQ Table
PL is based on JOOQ. If you are already familiar with JOOQ, this step should be a piece of cake. If not, let’s show you how to do it.
We shall have a table with 4 columns:
- creative_id (the ID as a primary key)
- status
- display_url
- headline
public class AdCreative extends AbstractDataTable<AdCreative> {
public static final AdCreative TABLE = new AdCreative("ad_creatives");
private AdCreative(String name) { super(name); }
public final TableField<Record, Integer> creative_id = createPKField("creative_id", SQLDataType.INTEGER.identity(true));
public final TableField<Record, String> status = createField("status", SQLDataType.VARCHAR.length(50));
public final TableField<Record, String> display_url = createField("display_url", SQLDataType.VARCHAR.length(255));
public final TableField<Record, String> headline = createField("headline", SQLDataType.VARCHAR.length(255));
}
- The
creative_id
field is defined as a PrimaryKey. At least one fields must be flagged as PK. - Note the
identity(true)
expression. It tells JOOQ that this column is auto-incrementing and we would like the generated IDs to be retrieved back to us so that we can use them. - This class doesn't create a table. We assume the table already exists. This step just defines a static definition of the table.
- AbstractDataTable is our infra class extending the JOOQ TableImpl class. Don’t use JOOQ directly without it (it won’t work).
You may feel some deja-vu as you look at it as it looks pretty much like the previous code block.
Every JOOQ field is now wrapped by a PL field. It may look redundant, but this allows more advanced features such as combining multiple tables into a single entity.
public class AdEntity extends AbstractEntityType<AdEntity> {
public static final AdEntity INSTANCE = new AdEntity();
private AdEntity() { super("ad"); }
@Override public DataTable getPrimaryTable() { return AdCreative.TABLE; }
@Immutable
public static final EntityField<AdEntity, Integer> ID = INSTANCE.field(AdCreative.TABLE.creative_id);
public static final EntityField<AdEntity, SyncStatus> SYNC_STATUS = INSTANCE.field(AdCreative.TABLE.status);
public static final EntityField<AdEntity, String> HEADLINE = INSTANCE.field(AdCreative.TABLE.headline);
public static final EntityField<AdEntity, String> URL = INSTANCE.field(AdCreative.TABLE.display_url);
}
For update
import static com.kenshoo.pl.entity.IdentifierType.uniqueKey;
public class UpdateAdCommand extends UpdateEntityCommand<AdEntity, Identifier<AdEntity>> {
public UpdateAdCommand(int idValue) {
super(AdEntity.INSTANCE, uniqueKey(AdEntity.Id).createIdentifier(idValue));
}
}
For create
public class CreateAdCommand extends CreateEntityCommand<AdEntity> {
public CreateAdCommand() {
super(AdEntity.INSTANCE);
}
}
public class AdPersistence {
private final PersistenceLayer persistenceLayer;
private final PLContext settings;
public AdPersistence(PLContext settings) {
this.settings = settings;
this.persistenceLayer = new PersistenceLayer(settings);
}
public CreateResult<AdEntity, Identifier<AdEntity>> create(Collection<CreateAdCommand> commands) {
return persistenceLayer.create(commands, flowBuilder().build());
}
public <ID extends Identifier<AdEntity>>
UpdateResult<AdEntity, ID> update(Collection<? extends UpdateEntityCommand<AdEntity, ID>> commands) {
return persistenceLayer.update(commands, flowBuilder().build());
}
public <ID extends Identifier<AdEntity>>
InsertOnDuplicateUpdateResult<KeywordEntity, ID> upsert(Collection<InsertOnDuplicateUpdateCommand<AdEntity, ID>> commands) {
return persistenceLayer.upsert(commands, flowBuilder().build());
}
public <ID extends Identifier<AdEntity>>
DeleteResult<KeywordEntity, ID> delete(Collection<DeleteEntityCommand<AdEntity, ID>> commands) {
return persistenceLayer.delete(commands, flowBuilder().build());
}
private ChangeFlowConfig.Builder<AdEntity> flowBuilder() {
//
// This is where we later add business rules to our flow.
//
return ChangeFlowConfigBuilderFactory.newInstance(settings, AdEntity.INSTANCE);
}
}
And the hard wiring is finally done!
We can start executing commands, define validators and have fun.
Setting up an Entity Persistence
Query language
Building the Flow
- Adding Simple Validators
- State Consumers
- Adding a Custom Validator
- Enrichers
- Output Generators
- Customizing Flows
- Primary and Secondary Tables (detailed example)
- State Consumers and Relations (what can we fetch)
- Child Commands
- Cascade Delete