GH-3 - Implement JDBC based repository
Signed-off-by: Björn Kieling <bkieling@vmware.com>
This commit is contained in:
committed by
Bjoern Kieling
parent
913ec7bb4e
commit
6a4260471e
@@ -16,6 +16,7 @@
|
||||
<modules>
|
||||
<module>spring-modulith-events-core</module>
|
||||
<module>spring-modulith-events-jpa</module>
|
||||
<module>spring-modulith-events-jdbc</module>
|
||||
<module>spring-modulith-events-jackson</module>
|
||||
<module>spring-modulith-events-tests</module>
|
||||
<module>spring-modulith-events-starter</module>
|
||||
|
||||
93
spring-modulith-events/spring-modulith-events-jdbc/pom.xml
Normal file
93
spring-modulith-events/spring-modulith-events-jdbc/pom.xml
Normal file
@@ -0,0 +1,93 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.experimental</groupId>
|
||||
<artifactId>spring-modulith-events</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<name>Spring Modulith - Events - JDBC-based registry</name>
|
||||
<artifactId>spring-modulith-events-jdbc</artifactId>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<module.name>org.springframework.modulith.events.jdbc</module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-modulith-events-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-jdbc</artifactId>
|
||||
</dependency>
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<!-- Testing -->
|
||||
|
||||
<!-- <dependency>
|
||||
<groupId>jakarta.xml.bind</groupId>
|
||||
<artifactId>jakarta.xml.bind-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>-->
|
||||
|
||||
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>jakarta.transaction</groupId>
|
||||
<artifactId>jakarta.transaction-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jdbc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.modulith.events.jdbc;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.support.MetaDataAccessException;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Initializes the DB schema used to store events
|
||||
*
|
||||
* @author Dmitry Belyaev, Björn Kieling
|
||||
*/
|
||||
public class DatabaseSchemaInitializer implements ResourceLoaderAware, InitializingBean {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
@Value("${spring.modulith.events.schema-initialization.enabled:false}")
|
||||
private boolean initEnabled;
|
||||
|
||||
public DatabaseSchemaInitializer(JdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws MetaDataAccessException {
|
||||
if (!initEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
DatabaseType databaseType = DatabaseType.fromMetaData(jdbcTemplate.getDataSource());
|
||||
String databaseName = databaseType.name().toLowerCase();
|
||||
var schemaDdlResource = resourceLoader.getResource("/schema-" + databaseName + ".sql");
|
||||
var schemaDdl = asString(schemaDdlResource);
|
||||
jdbcTemplate.execute(schemaDdl);
|
||||
}
|
||||
|
||||
private String asString(Resource resource) {
|
||||
try (Reader reader = new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)) {
|
||||
return FileCopyUtils.copyToString(reader);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright 2006-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.modulith.events.jdbc;
|
||||
|
||||
import org.springframework.jdbc.support.JdbcUtils;
|
||||
import org.springframework.jdbc.support.MetaDataAccessException;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Enum representing a database type, such as DB2 or oracle. The type also contains a
|
||||
* product name, which is expected to be the same as the product name provided by the
|
||||
* database driver's metadata.
|
||||
*
|
||||
* @author Lucas Ward
|
||||
*/
|
||||
public enum DatabaseType {
|
||||
|
||||
DERBY("Apache Derby"), DB2("DB2"), DB2VSE("DB2VSE"), DB2ZOS("DB2ZOS"), DB2AS400("DB2AS400"),
|
||||
HSQL("HSQL Database Engine"), SQLSERVER("Microsoft SQL Server"), MYSQL("MySQL"), ORACLE("Oracle"),
|
||||
POSTGRES("PostgreSQL"), SYBASE("Sybase"), H2("H2"), SQLITE("SQLite"), HANA("HDB");
|
||||
|
||||
private static final Map<String, DatabaseType> nameMap;
|
||||
|
||||
static {
|
||||
nameMap = new HashMap<>();
|
||||
for (DatabaseType type : values()) {
|
||||
nameMap.put(type.getProductName(), type);
|
||||
}
|
||||
}
|
||||
// A description is necessary due to the nature of database descriptions
|
||||
// in metadata.
|
||||
private final String productName;
|
||||
|
||||
private DatabaseType(String productName) {
|
||||
this.productName = productName;
|
||||
}
|
||||
|
||||
public String getProductName() {
|
||||
return productName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to obtain a DatabaseType from the provided product name.
|
||||
* @param productName {@link String} containing the product name.
|
||||
* @return the {@link DatabaseType} for given product name.
|
||||
* @throws IllegalArgumentException if none is found.
|
||||
*/
|
||||
public static DatabaseType fromProductName(String productName) {
|
||||
if (productName.equals("MariaDB"))
|
||||
productName = "MySQL";
|
||||
if (!nameMap.containsKey(productName)) {
|
||||
throw new IllegalArgumentException("DatabaseType not found for product name: [" + productName + "]");
|
||||
}
|
||||
else {
|
||||
return nameMap.get(productName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method that pulls a database product name from the DataSource's
|
||||
* metadata.
|
||||
* @param dataSource {@link DataSource} to the database to be used.
|
||||
* @return {@link DatabaseType} for the {@link DataSource} specified.
|
||||
* @throws MetaDataAccessException thrown if error occured during Metadata lookup.
|
||||
*/
|
||||
public static DatabaseType fromMetaData(DataSource dataSource) throws MetaDataAccessException {
|
||||
String databaseProductName = JdbcUtils.extractDatabaseMetaData(dataSource,
|
||||
DatabaseMetaData::getDatabaseProductName);
|
||||
if (StringUtils.hasText(databaseProductName) && databaseProductName.startsWith("DB2")) {
|
||||
String databaseProductVersion = JdbcUtils.extractDatabaseMetaData(dataSource,
|
||||
DatabaseMetaData::getDatabaseProductVersion);
|
||||
if (databaseProductVersion.startsWith("ARI")) {
|
||||
databaseProductName = "DB2VSE";
|
||||
}
|
||||
else if (databaseProductVersion.startsWith("DSN")) {
|
||||
databaseProductName = "DB2ZOS";
|
||||
}
|
||||
else if (databaseProductName.contains("AS")
|
||||
&& (databaseProductVersion.startsWith("QSQ") || databaseProductVersion
|
||||
.substring(databaseProductVersion.indexOf('V')).matches("V\\dR\\d[mM]\\d"))) {
|
||||
databaseProductName = "DB2AS400";
|
||||
}
|
||||
else {
|
||||
databaseProductName = JdbcUtils.commonDatabaseName(databaseProductName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
databaseProductName = JdbcUtils.commonDatabaseName(databaseProductName);
|
||||
}
|
||||
return fromProductName(databaseProductName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.modulith.events.jdbc;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.modulith.events.EventSerializer;
|
||||
import org.springframework.modulith.events.config.EventPublicationConfigurationExtension;
|
||||
|
||||
/**
|
||||
* @author Dmitry Belyaev, Björn Kieling
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
class JdbcEventPublicationAutoConfiguration implements EventPublicationConfigurationExtension {
|
||||
|
||||
@Bean
|
||||
public JdbcEventPublicationRepository jpaEventPublicationRepository(
|
||||
JdbcTemplate jdbcTemplate, EventSerializer serializer) {
|
||||
// TODO Why do we want to instantiate the serializer here and what
|
||||
// happens if no serializer is available or is not compatible to
|
||||
// JDBC serialization?
|
||||
return new JdbcEventPublicationRepository(jdbcTemplate, serializer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DatabaseSchemaInitializer databaseSchemaInitializer(JdbcTemplate jdbcTemplate) {
|
||||
return new DatabaseSchemaInitializer(jdbcTemplate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.modulith.events.jdbc;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.modulith.events.CompletableEventPublication;
|
||||
import org.springframework.modulith.events.EventPublication;
|
||||
import org.springframework.modulith.events.EventPublicationRepository;
|
||||
import org.springframework.modulith.events.EventSerializer;
|
||||
import org.springframework.modulith.events.PublicationTargetIdentifier;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Repository to store {@link EventPublication}s.
|
||||
*
|
||||
* @author Dmitry Belyaev, Björn Kieling
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class JdbcEventPublicationRepository implements EventPublicationRepository {
|
||||
|
||||
private static final String SQL_STATEMENT_INSERT = """
|
||||
INSERT INTO EVENT_PUBLICATION (ID, EVENT_TYPE, LISTENER_ID, PUBLICATION_DATE, SERIALIZED_EVENT)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""";
|
||||
private static final String SQL_STATEMENT_FIND_UNCOMPLETED = """
|
||||
SELECT ID, COMPLETION_DATE, EVENT_TYPE, LISTENER_ID, PUBLICATION_DATE, SERIALIZED_EVENT
|
||||
FROM EVENT_PUBLICATION
|
||||
WHERE COMPLETION_DATE IS NULL
|
||||
""";
|
||||
private static final String SQL_STATEMENT_UPDATE = """
|
||||
UPDATE EVENT_PUBLICATION
|
||||
SET COMPLETION_DATE = ?
|
||||
WHERE ID = ?
|
||||
""";
|
||||
private static final String SQL_STATEMENT_FIND_BY_EVENT_AND_LISTENER_ID = """
|
||||
SELECT * FROM EVENT_PUBLICATION
|
||||
WHERE SERIALIZED_EVENT = ? AND LISTENER_ID = ?
|
||||
ORDER BY PUBLICATION_DATE
|
||||
""";
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final EventSerializer serializer;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public EventPublication create(EventPublication publication) {
|
||||
String serializedEvent = serializeEvent(publication.getEvent());
|
||||
jdbcTemplate.update(SQL_STATEMENT_INSERT, //
|
||||
UUID.randomUUID(), //
|
||||
publication.getEvent().getClass().getName(), //
|
||||
publication.getTargetIdentifier().getValue(), //
|
||||
publication.getPublicationDate(), //
|
||||
serializedEvent);
|
||||
|
||||
return publication;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public EventPublication updateCompletionDate(CompletableEventPublication publication) {
|
||||
String serializedEvent = serializeEvent(publication.getEvent());
|
||||
List<UUID> results = jdbcTemplate.query(SQL_STATEMENT_FIND_BY_EVENT_AND_LISTENER_ID,
|
||||
(rs, rowNum) -> rs.getObject("ID", UUID.class), serializedEvent, publication.getTargetIdentifier().getValue());
|
||||
if (!results.isEmpty()) {
|
||||
jdbcTemplate.update(SQL_STATEMENT_UPDATE, publication.getCompletionDate().orElse(null), results.get(0));
|
||||
}
|
||||
|
||||
return publication;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<EventPublication> findByCompletionDateIsNull() {
|
||||
return jdbcTemplate.query(SQL_STATEMENT_FIND_UNCOMPLETED, this::mapResultSetToEventPublications);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Optional<EventPublication> findByEventAndTargetIdentifier(Object event,
|
||||
PublicationTargetIdentifier targetIdentifier) {
|
||||
|
||||
String serializedEvent = serializeEvent(event);
|
||||
List<EventPublication> results = jdbcTemplate.query(SQL_STATEMENT_FIND_BY_EVENT_AND_LISTENER_ID,
|
||||
this::mapResultSetToEventPublications, serializedEvent, targetIdentifier.getValue());
|
||||
if (results.isEmpty()) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
// if there are several events with exactly the same payload we return the oldest one first
|
||||
return Optional.of(results.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
private String serializeEvent(Object event) {
|
||||
return serializer.serialize(event).toString();
|
||||
}
|
||||
|
||||
private List<EventPublication> mapResultSetToEventPublications(ResultSet rs) throws SQLException {
|
||||
var result = new ArrayList<EventPublication>();
|
||||
while (rs.next()) {
|
||||
entityToDomain(rs).ifPresent(result::add);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Optional<EventPublication> entityToDomain(ResultSet rs) throws SQLException {
|
||||
var id = rs.getObject("ID", UUID.class);
|
||||
var eventClassName = rs.getString("EVENT_TYPE");
|
||||
Class<?> eventClass;
|
||||
try {
|
||||
eventClass = Class.forName(eventClassName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
LOG.warn("Event '{}' of unknown type '{}' found", id, eventClassName);
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(JdbcEventPublication.builder()
|
||||
.completionDate(Optional.ofNullable(rs.getTimestamp("COMPLETION_DATE")).map(Timestamp::toInstant).orElse(null))
|
||||
.eventType(eventClass) //
|
||||
.listenerId(rs.getString("LISTENER_ID")) //
|
||||
.publicationDate(rs.getTimestamp("PUBLICATION_DATE").toInstant()) //
|
||||
.serializedEvent(rs.getString("SERIALIZED_EVENT")) //
|
||||
.serializer(serializer) //
|
||||
.build());
|
||||
}
|
||||
|
||||
@EqualsAndHashCode
|
||||
@Builder
|
||||
private static class JdbcEventPublication implements CompletableEventPublication {
|
||||
|
||||
private final UUID id;
|
||||
private final Instant publicationDate;
|
||||
private final String listenerId;
|
||||
private final String serializedEvent;
|
||||
private final Class<?> eventType;
|
||||
|
||||
private final EventSerializer serializer;
|
||||
private Instant completionDate;
|
||||
|
||||
@Override
|
||||
public Object getEvent() {
|
||||
return serializer.deserialize(serializedEvent, eventType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicationTargetIdentifier getTargetIdentifier() {
|
||||
return PublicationTargetIdentifier.of(listenerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getPublicationDate() {
|
||||
return publicationDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Instant> getCompletionDate() {
|
||||
return Optional.ofNullable(completionDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPublicationCompleted() {
|
||||
return completionDate != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableEventPublication markCompleted() {
|
||||
this.completionDate = Instant.now();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.modulith.events.jdbc.JdbcEventPublicationAutoConfiguration
|
||||
@@ -0,0 +1,10 @@
|
||||
CREATE TABLE IF NOT EXISTS EVENT_PUBLICATION
|
||||
(
|
||||
ID UUID NOT NULL,
|
||||
COMPLETION_DATE TIMESTAMP(9) WITH TIME ZONE,
|
||||
EVENT_TYPE VARCHAR(512) NOT NULL,
|
||||
LISTENER_ID VARCHAR(512) NOT NULL,
|
||||
PUBLICATION_DATE TIMESTAMP(9) WITH TIME ZONE NOT NULL,
|
||||
SERIALIZED_EVENT VARCHAR(4000) NOT NULL,
|
||||
PRIMARY KEY (ID)
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
CREATE TABLE IF NOT EXISTS EVENT_PUBLICATION
|
||||
(
|
||||
ID UUID NOT NULL,
|
||||
COMPLETION_DATE TIMESTAMP(9),
|
||||
EVENT_TYPE VARCHAR(512) NOT NULL,
|
||||
LISTENER_ID VARCHAR(512) NOT NULL,
|
||||
PUBLICATION_DATE TIMESTAMP(9) NOT NULL,
|
||||
SERIALIZED_EVENT VARCHAR(4000) NOT NULL,
|
||||
PRIMARY KEY (ID)
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
CREATE TABLE IF NOT EXISTS EVENT_PUBLICATION
|
||||
(
|
||||
ID UUID NOT NULL,
|
||||
COMPLETION_DATE TIMESTAMP(9) WITH TIME ZONE,
|
||||
EVENT_TYPE VARCHAR(512) NOT NULL,
|
||||
LISTENER_ID VARCHAR(512) NOT NULL,
|
||||
PUBLICATION_DATE TIMESTAMP(9) WITH TIME ZONE NOT NULL,
|
||||
SERIALIZED_EVENT VARCHAR(4000) NOT NULL,
|
||||
PRIMARY KEY (ID)
|
||||
)
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.modulith.events.jdbc;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.modulith.events.EventPublicationRegistry;
|
||||
import org.springframework.modulith.events.EventSerializer;
|
||||
import org.springframework.modulith.testapp.TestApplication;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Dmitry Belyaev, Björn Kieling
|
||||
*/
|
||||
@SpringBootTest(
|
||||
classes = TestApplication.class,
|
||||
properties = "spring.modulith.events.schema-initialization.enabled=true"
|
||||
)
|
||||
public class JdbcEventPublicationAutoConfigurationIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@MockBean
|
||||
private EventSerializer serializer;
|
||||
|
||||
@Test
|
||||
void bootstrapsApplicationComponents() {
|
||||
|
||||
assertThat(context.getBean(EventPublicationRegistry.class)).isNotNull();
|
||||
assertThat(context.getBean(JdbcEventPublicationRepository.class)).isNotNull();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.modulith.testapp;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.data.jdbc.DataJdbcTest;
|
||||
import org.springframework.boot.test.mock.mockito.SpyBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.modulith.events.jdbc.DatabaseSchemaInitializer;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
|
||||
/**
|
||||
* @author Dmitry Belyaev, Björn Kieling
|
||||
*/
|
||||
public class DatabaseSchemaInitializerTest {
|
||||
|
||||
@Nested
|
||||
@DataJdbcTest(properties = {
|
||||
"spring.modulith.events.schema-initialization.enabled=true"
|
||||
})
|
||||
@Import(DatabaseSchemaInitializer.class)
|
||||
class InitializationEnabled {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Test
|
||||
public void shouldCreateDatabaseSchemaOnStartUp() {
|
||||
Long count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM EVENT_PUBLICATION", Long.class);
|
||||
|
||||
assertThat(count).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DataJdbcTest(properties = {
|
||||
"spring.modulith.events.schema-initialization.enabled=false"
|
||||
})
|
||||
@Import(DatabaseSchemaInitializer.class)
|
||||
class InitializationDisabled {
|
||||
|
||||
@SpyBean
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Test
|
||||
public void shouldNotCreateDatabaseSchemaOnStartUp() {
|
||||
Mockito.verify(jdbcTemplate, Mockito.never()).execute(anyString());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DataJdbcTest
|
||||
@Import(DatabaseSchemaInitializer.class)
|
||||
class InitializationDisabledByDefault {
|
||||
|
||||
@SpyBean
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Test
|
||||
public void shouldNotCreateDatabaseSchemaOnStartUp() {
|
||||
Mockito.verify(jdbcTemplate, Mockito.never()).execute(anyString());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DataJdbcTest(properties = {
|
||||
"spring.modulith.events.schema-initialization.enabled=true"
|
||||
})
|
||||
@ActiveProfiles("hsql")
|
||||
@Import(DatabaseSchemaInitializer.class)
|
||||
class InitializationUseHsql {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Test
|
||||
public void shouldCreateDatabaseSchemaOnStartUp() {
|
||||
Long count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM EVENT_PUBLICATION", Long.class);
|
||||
|
||||
assertThat(count).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DataJdbcTest(properties = {
|
||||
"spring.modulith.events.schema-initialization.enabled=true"
|
||||
})
|
||||
@ActiveProfiles("h2")
|
||||
@Import(DatabaseSchemaInitializer.class)
|
||||
class InitializationUseH2 {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Test
|
||||
public void shouldCreateDatabaseSchemaOnStartUp() {
|
||||
Long count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM EVENT_PUBLICATION", Long.class);
|
||||
|
||||
assertThat(count).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@Disabled
|
||||
@DataJdbcTest(properties = {
|
||||
"spring.modulith.events.schema-initialization.enabled=true"
|
||||
})
|
||||
@ActiveProfiles("postgres")
|
||||
@Import(DatabaseSchemaInitializer.class)
|
||||
class InitializationUsePostgres {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Test
|
||||
public void shouldCreateDatabaseSchemaOnStartUp() {
|
||||
Long count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM EVENT_PUBLICATION", Long.class);
|
||||
|
||||
assertThat(count).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.modulith.testapp;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.data.jdbc.DataJdbcTest;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.modulith.events.CompletableEventPublication;
|
||||
import org.springframework.modulith.events.EventPublication;
|
||||
import org.springframework.modulith.events.EventSerializer;
|
||||
import org.springframework.modulith.events.PublicationTargetIdentifier;
|
||||
import org.springframework.modulith.events.jdbc.DatabaseSchemaInitializer;
|
||||
import org.springframework.modulith.events.jdbc.JdbcEventPublicationRepository;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
/**
|
||||
* @author Dmitry Belyaev, Björn Kieling
|
||||
*/
|
||||
class JdbcEventPublicationRepositoryIntegrationTests {
|
||||
|
||||
private static final PublicationTargetIdentifier TARGET_IDENTIFIER = PublicationTargetIdentifier.of("listener");
|
||||
|
||||
private JdbcEventPublicationRepository repository;
|
||||
|
||||
private final EventSerializer eventSerializer = mock(EventSerializer.class);
|
||||
|
||||
private abstract class TestBase {
|
||||
|
||||
@Autowired
|
||||
protected JdbcTemplate jdbcTemplate;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
repository = new JdbcEventPublicationRepository(jdbcTemplate, eventSerializer);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DataJdbcTest
|
||||
@ActiveProfiles("hsql")
|
||||
@Import(DatabaseSchemaInitializer.class)
|
||||
class HSQL extends TestBase {
|
||||
|
||||
@Nested
|
||||
class CreateAndUpdate {
|
||||
|
||||
@Test
|
||||
void shouldPersistAndUpdateEventPublication() {
|
||||
shouldPersistAndUpdateEventPublicationTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateSingleEventPublication() {
|
||||
shouldUpdateSingleEventPublicationTest();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FindByCompletionDateIsNull {
|
||||
|
||||
@Test
|
||||
void shouldSilentlyIgnoreNotSerializableEvents() {
|
||||
shouldSilentlyIgnoreNotSerializableEventsTest(jdbcTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FindBySerializedEventAndListenerId {
|
||||
|
||||
@Test
|
||||
void shouldTolerateEmptyResult() {
|
||||
shouldTolerateEmptyResultTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTheOldestEvent() throws InterruptedException {
|
||||
shouldReturnTheOldestEventTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSilentlyIgnoreNotSerializableEvents() {
|
||||
shouldSilentlyIgnoreNotSerializableEventsTest(jdbcTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@DataJdbcTest
|
||||
@ActiveProfiles("h2")
|
||||
@Import(DatabaseSchemaInitializer.class)
|
||||
class H2 extends TestBase {
|
||||
|
||||
@Nested
|
||||
class CreateAndUpdate {
|
||||
|
||||
@Test
|
||||
void shouldPersistAndUpdateEventPublication() {
|
||||
shouldPersistAndUpdateEventPublicationTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateSingleEventPublication() {
|
||||
shouldUpdateSingleEventPublicationTest();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FindByCompletionDateIsNull {
|
||||
|
||||
@Test
|
||||
void shouldSilentlyIgnoreNotSerializableEvents() {
|
||||
shouldSilentlyIgnoreNotSerializableEventsTest(jdbcTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FindBySerializedEventAndListenerId {
|
||||
|
||||
@Test
|
||||
void shouldTolerateEmptyResult() {
|
||||
shouldTolerateEmptyResultTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTheOldestEvent() throws InterruptedException {
|
||||
shouldReturnTheOldestEventTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSilentlyIgnoreNotSerializableEvents() {
|
||||
shouldSilentlyIgnoreNotSerializableEventsTest(jdbcTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@Disabled
|
||||
@DataJdbcTest
|
||||
@ActiveProfiles("postgres")
|
||||
@Import(DatabaseSchemaInitializer.class)
|
||||
class Postgres extends TestBase {
|
||||
|
||||
@Nested
|
||||
class CreateAndUpdate {
|
||||
|
||||
@Test
|
||||
void shouldPersistAndUpdateEventPublication() {
|
||||
shouldPersistAndUpdateEventPublicationTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateSingleEventPublication() {
|
||||
shouldUpdateSingleEventPublicationTest();
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FindByCompletionDateIsNull {
|
||||
|
||||
@Test
|
||||
void shouldSilentlyIgnoreNotSerializableEvents() {
|
||||
shouldSilentlyIgnoreNotSerializableEventsTest(jdbcTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class FindBySerializedEventAndListenerId {
|
||||
|
||||
@Test
|
||||
void shouldTolerateEmptyResult() {
|
||||
shouldTolerateEmptyResultTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTheOldestEvent() throws InterruptedException {
|
||||
shouldReturnTheOldestEventTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSilentlyIgnoreNotSerializableEvents() {
|
||||
shouldSilentlyIgnoreNotSerializableEventsTest(jdbcTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void shouldPersistAndUpdateEventPublicationTest() {
|
||||
TestEvent testEvent = new TestEvent("id");
|
||||
String serializedEvent = "{\"eventId\":\"id\"}";
|
||||
|
||||
when(eventSerializer.serialize(testEvent)).thenReturn(serializedEvent);
|
||||
when(eventSerializer.deserialize(serializedEvent, TestEvent.class)).thenReturn(testEvent);
|
||||
|
||||
CompletableEventPublication publication = CompletableEventPublication.of(testEvent, TARGET_IDENTIFIER);
|
||||
|
||||
// Store publication
|
||||
repository.create(publication);
|
||||
|
||||
List<EventPublication> eventPublications = repository.findByCompletionDateIsNull();
|
||||
assertThat(eventPublications).hasSize(1);
|
||||
assertThat(eventPublications.get(0).getEvent()).isEqualTo(publication.getEvent());
|
||||
assertThat(eventPublications.get(0).getTargetIdentifier()).isEqualTo(publication.getTargetIdentifier());
|
||||
assertThat(repository.findByEventAndTargetIdentifier(testEvent, TARGET_IDENTIFIER))
|
||||
.isPresent();
|
||||
|
||||
// Complete publication
|
||||
repository.updateCompletionDate(publication.markCompleted());
|
||||
|
||||
assertThat(repository.findByCompletionDateIsNull()).isEmpty();
|
||||
}
|
||||
|
||||
private void shouldUpdateSingleEventPublicationTest() {
|
||||
TestEvent testEvent1 = new TestEvent("id1");
|
||||
TestEvent testEvent2 = new TestEvent("id2");
|
||||
String serializedEvent1 = "{\"eventId\":\"id1\"}";
|
||||
String serializedEvent2 = "{\"eventId\":\"id2\"}";
|
||||
|
||||
when(eventSerializer.serialize(testEvent1)).thenReturn(serializedEvent1);
|
||||
when(eventSerializer.deserialize(serializedEvent1, TestEvent.class)).thenReturn(testEvent1);
|
||||
when(eventSerializer.serialize(testEvent2)).thenReturn(serializedEvent2);
|
||||
when(eventSerializer.deserialize(serializedEvent2, TestEvent.class)).thenReturn(testEvent2);
|
||||
|
||||
CompletableEventPublication publication1 = CompletableEventPublication.of(testEvent1, TARGET_IDENTIFIER);
|
||||
CompletableEventPublication publication2 = CompletableEventPublication.of(testEvent2, TARGET_IDENTIFIER);
|
||||
|
||||
// Store publication
|
||||
repository.create(publication1);
|
||||
repository.create(publication2);
|
||||
|
||||
// Complete publication
|
||||
repository.updateCompletionDate(publication2.markCompleted());
|
||||
|
||||
List<EventPublication> withCompletionDateNull = repository.findByCompletionDateIsNull();
|
||||
assertThat(withCompletionDateNull).hasSize(1);
|
||||
assertThat(withCompletionDateNull.get(0).getEvent()).isEqualTo(testEvent1);
|
||||
}
|
||||
|
||||
private void shouldTolerateEmptyResultTest() {
|
||||
TestEvent testEvent = new TestEvent("id");
|
||||
String serializedEvent = "{\"eventId\":\"id\"}";
|
||||
when(eventSerializer.serialize(testEvent)).thenReturn(serializedEvent);
|
||||
|
||||
Optional<EventPublication> actual =
|
||||
repository.findByEventAndTargetIdentifier(testEvent, TARGET_IDENTIFIER);
|
||||
|
||||
assertThat(actual).isEmpty();
|
||||
}
|
||||
|
||||
private void shouldReturnTheOldestEventTest() throws InterruptedException {
|
||||
TestEvent testEvent = new TestEvent("id");
|
||||
String serializedEvent = "{\"eventId\":\"id\"}";
|
||||
when(eventSerializer.serialize(testEvent)).thenReturn(serializedEvent);
|
||||
when(eventSerializer.deserialize(serializedEvent, TestEvent.class)).thenReturn(testEvent);
|
||||
|
||||
CompletableEventPublication publicationOld = CompletableEventPublication.of(testEvent, TARGET_IDENTIFIER);
|
||||
Thread.sleep(10);
|
||||
CompletableEventPublication publicationNew = CompletableEventPublication.of(testEvent, TARGET_IDENTIFIER);
|
||||
|
||||
repository.create(publicationNew);
|
||||
repository.create(publicationOld);
|
||||
|
||||
|
||||
Optional<EventPublication> actual =
|
||||
repository.findByEventAndTargetIdentifier(testEvent, TARGET_IDENTIFIER);
|
||||
|
||||
assertThat(actual).isNotEmpty();
|
||||
assertThat(actual.get().getPublicationDate()).isEqualTo(publicationOld.getPublicationDate());
|
||||
}
|
||||
|
||||
private void shouldSilentlyIgnoreNotSerializableEventsTest(JdbcTemplate jdbcTemplate) {
|
||||
TestEvent testEvent = new TestEvent("id");
|
||||
String serializedEvent = "{\"eventId\":\"id\"}";
|
||||
when(eventSerializer.serialize(testEvent)).thenReturn(serializedEvent);
|
||||
when(eventSerializer.deserialize(serializedEvent, TestEvent.class)).thenReturn(testEvent);
|
||||
|
||||
CompletableEventPublication publication = CompletableEventPublication.of(testEvent, TARGET_IDENTIFIER);
|
||||
|
||||
// Store publication
|
||||
repository.create(publication);
|
||||
jdbcTemplate.update("UPDATE EVENT_PUBLICATION SET EVENT_TYPE='abc'");
|
||||
|
||||
Optional<EventPublication> actual =
|
||||
repository.findByEventAndTargetIdentifier(testEvent, TARGET_IDENTIFIER);
|
||||
|
||||
assertThat(actual).isEmpty();
|
||||
}
|
||||
|
||||
private static final class TestEvent {
|
||||
private final String eventId;
|
||||
|
||||
private TestEvent(String eventId) {
|
||||
this.eventId = eventId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.modulith.testapp;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author Dmitry Belyaev, Björn Kieling
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class TestApplication {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.test.database.replace=NONE
|
||||
|
||||
spring.modulith.events.schema-initialization.enabled=true
|
||||
@@ -0,0 +1,5 @@
|
||||
spring.datasource.driverClassName=org.hsqldb.jdbc.JDBCDriver
|
||||
spring.datasource.url=jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1
|
||||
spring.test.database.replace=NONE
|
||||
|
||||
spring.modulith.events.schema-initialization.enabled=true
|
||||
@@ -0,0 +1,7 @@
|
||||
spring.datasource.driverClassName=org.postgresql.Driver
|
||||
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
|
||||
spring.datasource.username=username
|
||||
spring.datasource.password=password
|
||||
spring.test.database.replace=NONE
|
||||
|
||||
spring.modulith.events.schema-initialization.enabled=true
|
||||
Reference in New Issue
Block a user