/** * Returns a comma separated list of tracked domain event column names to select from an event entry. * * @return comma separated tracked domain event column names. */ protected String trackedEventFields() { return schema.globalIndexColumn() + ", " + domainEventFields(); }
/** * Returns a new {@link Builder} initialized with default settings. * * @return a new builder for the event schema */ public static Builder builder() { return new Builder(); }
/** * Initializes a {@link JdbcEventStorageEngine} as specified through this Builder. * * @return a {@link JdbcEventStorageEngine} as specified through this Builder */ public JdbcEventStorageEngine build() { return new JdbcEventStorageEngine(this); }
/** * Returns a comma separated list of domain event column names to select from an event or snapshot entry. * * @return comma separated domain event column names. */ protected String domainEventFields() { return String.join(", ", schema.eventIdentifierColumn(), schema.timestampColumn(), schema.payloadTypeColumn(), schema.payloadRevisionColumn(), schema.payloadColumn(), schema.metaDataColumn(), schema.typeColumn(), schema.aggregateIdentifierColumn(), schema.sequenceNumberColumn()); }
/** * Creates a statement to read the snapshot entry of an aggregate with given identifier. * * @param connection The connection to the database. * @param identifier The aggregate identifier. * @return A {@link PreparedStatement} that returns the last snapshot entry of the aggregate (if any) when executed. * * @throws SQLException when an exception occurs while creating the prepared statement. */ protected PreparedStatement readSnapshotData(Connection connection, String identifier) throws SQLException { final String s = "SELECT " + domainEventFields() + " FROM " + schema.snapshotTable() + " WHERE " + schema.aggregateIdentifierColumn() + " = ? ORDER BY " + schema.sequenceNumberColumn() + " DESC"; PreparedStatement statement = connection.prepareStatement(s); statement.setString(1, identifier); return statement; }
public JdbcEventStoreBenchmark(DataSource dataSource, PlatformTransactionManager transactionManager) { super(JdbcEventStorageEngine.builder() .connectionProvider(new UnitOfWorkAwareConnectionProviderWrapper(dataSource::getConnection)) .transactionManager(NoTransactionManager.INSTANCE) .build()); this.dataSource = dataSource; this.transactionManager = transactionManager; }
/** * Performs the DDL queries to create the schema necessary for this storage engine implementation. * * @param schemaFactory Factory of the event schema. * @throws EventStoreException when an error occurs executing SQL statements. */ public void createSchema(EventTableFactory schemaFactory) { executeUpdates(getConnection(), e -> { throw new EventStoreException("Failed to create event tables", e); }, connection -> schemaFactory.createDomainEventTable(connection, schema), connection -> schemaFactory.createSnapshotEventTable(connection, schema)); }
private Builder() { persistenceExceptionResolver(new JdbcSQLErrorCodesResolver()); }
/** * Builds a new {@link EventSchema} from builder values. * * @return new EventSchema */ public EventSchema build() { return new EventSchema(this); } }
@Before public void setUp() { testSubject = new Oracle11EventTableFactory(); eventSchema = new EventSchema(); try { connection = DriverManager.getConnection("jdbc:oracle:thin:@//localhost:1521/xe", "system", "oracle"); } catch (SQLException e) { assumeNoException("Ignoring test. Machine does not have a local Oracle 11 instance running", e); } }
@Test public void testCreateDomainEventTable() throws Exception { // test passes if no exception is thrown testSubject.createDomainEventTable(connection, eventSchema) .execute(); connection.prepareStatement("SELECT * FROM " + eventSchema.domainEventTable()) .execute(); connection.prepareStatement("DROP TABLE " + eventSchema.domainEventTable()) .execute(); connection.prepareStatement("DROP SEQUENCE " + eventSchema.domainEventTable() + "_seq") .execute(); }
@Test public void testCreateSnapshotEventTable() throws Exception { // test passes if no exception is thrown testSubject.createSnapshotEventTable(connection, eventSchema) .execute(); connection.prepareStatement("SELECT * FROM " + eventSchema.snapshotTable()) .execute(); connection.prepareStatement("DROP TABLE " + eventSchema.snapshotTable()) .execute(); } }
@Override public boolean isDuplicateKeyViolation(Exception exception) { return causeIsEntityExistsException(exception); }
/** * Initializes the default Event Schema */ public EventSchema() { this(builder()); }
/** * Sets the threshold of number of gaps in a token before an attempt to clean gaps up is taken. Defaults to an * integer of {@code 250} ({@link JdbcEventStorageEngine#DEFAULT_GAP_CLEANING_THRESHOLD}). * * @param gapCleaningThreshold an {@code int} specifying the threshold of number of gaps in a token before an * attempt to clean gaps up is taken * @return the current Builder instance, for fluent interfacing */ public Builder gapCleaningThreshold(int gapCleaningThreshold) { assertPositive(gapCleaningThreshold, "gapCleaningThreshold"); this.gapCleaningThreshold = gapCleaningThreshold; return this; }
@Override public PreparedStatement createSnapshotEventTable(Connection connection, EventSchema schema) throws SQLException { String sql = "CREATE TABLE " + schema.snapshotTable() + " (\n" + schema.aggregateIdentifierColumn() + " VARCHAR(255) NOT NULL,\n" + schema.sequenceNumberColumn() + " NUMBER(19) NOT NULL,\n" + schema.typeColumn() + " VARCHAR(255) NOT NULL,\n" + schema.eventIdentifierColumn() + " VARCHAR(255) NOT NULL,\n" + schema.metaDataColumn() + " " + payloadType() + ",\n" + schema.payloadColumn() + " " + payloadType() + " NOT NULL,\n" + schema.payloadRevisionColumn() + " VARCHAR(255),\n" + schema.payloadTypeColumn() + " VARCHAR(255) NOT NULL,\n" + schema.timestampColumn() + " VARCHAR(255) NOT NULL,\n" + "PRIMARY KEY (" + schema.aggregateIdentifierColumn() + ", " + schema.sequenceNumberColumn() + "),\n" + "UNIQUE (" + schema.eventIdentifierColumn() + ")\n" + ")"; return connection.prepareStatement(sql); }
private boolean causeIsEntityExistsException(Throwable exception) { return exception instanceof java.sql.SQLIntegrityConstraintViolationException || (exception.getCause() != null && causeIsEntityExistsException(exception.getCause())); }
/** * Sets the amount of time until a 'gap' in a TrackingToken may be considered timed out. This setting will * affect the cleaning process of gaps. Gaps that have timed out will be removed from Tracking Tokens to improve * performance of reading events. Defaults to an integer of {@code 60000} * ({@link JdbcEventStorageEngine#DEFAULT_GAP_TIMEOUT}), thus 1 minute. * * @param gapTimeout an {@code int} specifying the amount of time until a 'gap' in a TrackingToken may be * considered timed out * @return the current Builder instance, for fluent interfacing */ public Builder gapTimeout(int gapTimeout) { assertPositive(gapTimeout, "gapTimeout"); this.gapTimeout = gapTimeout; return this; }
@Override public PreparedStatement createSnapshotEventTable(Connection connection, EventSchema schema) throws SQLException { String sql = "CREATE TABLE IF NOT EXISTS " + schema.snapshotTable() + " (\n" + schema.aggregateIdentifierColumn() + " VARCHAR(255) NOT NULL,\n" + schema.sequenceNumberColumn() + " BIGINT NOT NULL,\n" + schema.typeColumn() + " VARCHAR(255) NOT NULL,\n" + schema.eventIdentifierColumn() + " VARCHAR(255) NOT NULL,\n" + schema.metaDataColumn() + " " + payloadType() + ",\n" + schema.payloadColumn() + " " + payloadType() + " NOT NULL,\n" + schema.payloadRevisionColumn() + " VARCHAR(255),\n" + schema.payloadTypeColumn() + " VARCHAR(255) NOT NULL,\n" + schema.timestampColumn() + " VARCHAR(255) NOT NULL,\n" + "PRIMARY KEY (" + schema.aggregateIdentifierColumn() + ", " + schema.sequenceNumberColumn() + "),\n" + "UNIQUE (" + schema.eventIdentifierColumn() + ")\n" + ")"; return connection.prepareStatement(sql); }
/** * Sets the {@code maxGapOffset} specifying the maximum distance in sequence numbers between a missing event and * the event with the highest known index. If the gap is bigger it is assumed that the missing event will not be * committed to the store anymore. This event storage engine will no longer look for those events the next time * a batch is fetched. Defaults to an integer of {@code 10000} * ({@link JdbcEventStorageEngine#DEFAULT_MAX_GAP_OFFSET}. * * @param maxGapOffset an {@code int} specifying the maximum distance in sequence numbers between a missing * event and the event with the highest known index * @return the current Builder instance, for fluent interfacing */ public Builder maxGapOffset(int maxGapOffset) { assertPositive(maxGapOffset, "maxGapOffset"); this.maxGapOffset = maxGapOffset; return this; }