Is there a way to run Flyway Java-based callbacks with Spring boot?
I'm converting an existing project that after each migration updates some view definitions, and this is done by Java as it needs some extra logic. I know it could be done in pl/pgsql (we are using Postgres) but it is already done and tested in Java.
Spring boot docs says it is possible, but it is listed that the callback scripts should live in same dir as migrations, maybe this works just for SQL based callbacks.
This code works without Spring Boot:
Flyway flyway = new Flyway();
flyway.setDataSource(this.getDataSource());
flyway.setLocations("/db/migration");
flyway.setCallbacks(new LogMaintenanceFlywayCallback());
flyway.migrate();
I have several migrations in /db/migration and after each one I need to execute my callback. It works in my current project and I need to do the same (or another way to get the same behavior) in Spring Boot.
You can have a configuration like this and it will work:
#Configuration
public class FlywayFactory {
#Bean
public FlywayMigrationInitializer flywayInitializer(Flyway flyway, FlywayCallback flywayCallback) {
flyway.setCallbacks(flywayCallback);
return new FlywayMigrationInitializer(flyway);
}
#Bean
public FlywayCallback flywayCallback() {
return new LogMaintenanceFlywayCallback();
}
}
Since method setCallbacks(Callback... callbacks) of the Flyway has been deprecated and will be removed in Flyway 6.0, you can use new API and FlywayConfigurationCustomizer to set up custom Java-based callbacks. Then the configuration is as below:
#Configuration
public class FlywayFactory {
#Bean
public FlywayConfigurationCustomizer flywayConfigurationCustomizer() {
return configuration -> configuration.callbacks(new LogMaintenanceFlywayCallback());
}
}
There seems to be no possibility to set the callbacks in the Spring Boot autoconfiguration (See FlywayAutoConfiguration.java)
There are 2 things you can do:
Create your own Flyway instance in one of your Configuration classes. Spring Boot will not create his instance in case you do that.
Autowire the Flyway instance in one of your Configuration classes and call the setCallbacks method in a PostConstruct method (But it might be tricky to make sure you call the setter before the migration starts)
You can override the Flyway migration stragtey
#Component
public class CallbackFlywayMigrationStrategy implements FlywayMigrationStrategy {
#Override
public void migrate(Flyway flyway) {
flyway.setCallbacks(new LogMaintenanceFlywayCallback());
flyway.migrate();
}
}
You can define a bean of type org.flywaydb.core.api.callback.Callback as follows:
#Bean
public Callback logMaintenanceFlywayCallback() {
return new LogMaintenanceFlywayCallback();
}
Related
I am trying to implement a project using jobrunr. I have a use case where a service I have written should be triggered once the maximum retries are done for a job. I tried achieving the same using this answer as reference. The filter logic is triggered once a job fails but the dependency I include (which has my logic) is returning a null point exception(java.lang.NullPointerException: Cannot invoke "com.project.service.ScheduleHistoryService.someFunc()" because "this.service" is null). I am able to inject the same service file using #Autowire in my other components.
What am I doing wrong here?
I am using jobrunr version 5.1.4.
Attached is a screenshot of the sample code:enter image description here
Injecting services in the filters is only possible in the Pro version of JobRunr.
My hack / workaround for this is injecting the Service in the regarding Configuration and passing it to the constructor of a CustomRetryFilter:
#Configuration
public class JobRunrConfig {
#Bean
public BackgroundJobServer backgroundJobServer(
StorageProvider storageProvider, JsonMapper jobRunrJsonMapper, JobActivator jobActivator, BackgroundJobServerConfiguration backgroundJobServerConfiguration, JobRunrProperties properties,
ApplicationEventPublisher applicationEventPublisher) {
BackgroundJobServer backgroundJobServer = new BackgroundJobServer(
storageProvider, jobRunrJsonMapper, jobActivator, backgroundJobServerConfiguration);
backgroundJobServer.setJobFilters(
Collections.singletonList(
new CustomRetryFilter(
applicationEventPublisher,
properties.getJobs().getDefaultNumberOfRetries(),
properties.getJobs().getRetryBackOffTimeSeed())));
backgroundJobServer.start();
return backgroundJobServer;
}
}
Assume I want to integration test code relying on a JPA datasource in a Spring Boot 2.x application with a PostgreSQL testcontainer (great tool for managing Docker containers from within test classes with one or few more lines of code). Assume further that I'm managing the ports (included in the JDBC URL) in application.properties, e.g.
spring.datasource.url=jdbc:postgresql://user-service-postgres:5432/user_service
In the integration test I create testcontainers with
#Rule
PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer();
In a preparation method I can access the value I want to set for spring.datasource.url with
postgreSQLContainer.getJdbcUrl()
How to tell Spring Boot in the test to use that URL instead of the one specified in application.properties.
I'd like to stick to my property files in order to minimize changes, but I'm thankful for other approaches including an explanation why they're superior or necessary as well.
I'm using Spring Boot 2.x.
Since Spring Framework 5.2.5 (Spring Boot 2.2.6) this setup is now even simpler as we can use the #DynamicPropertySource annotation and don't have to write and register a custom initializer.
Assuming you use the JUnit 5 dependency of Testcontainers, your test can look like the following:
#SpringBootTest
#Testcontainers
class ExampleIntegrationTests {
#Container
static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer();
#DynamicPropertySource
static void dataSourceProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl);
}
}
You can read more about this new feature here. I've also covered the different application properties setup ways (depending on Spring Boot and JUnit version) in a dedicated Testcontainers guide.
You can manually override the property from within your Spring-boot test by using ContextConfiguration and ApplicationContextInitializer.
Override the property - define a static inner class:
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
TestPropertyValues
.of("spring.datasource.url=" + postgreSQLContainer.getJdbcUrl())
.applyTo(configurableApplicationContext.getEnvironment());
}
}
ApplicationContextInitializer can be used for programmatically initializing a Spring context before context refresh. Now, wire up the context initializer class by annotating at test class level with ContextConfiguration:
#ContextConfiguration(initializers = Initializer.class)
Docs:
ApplicationContextInitializer
ContextConfiguration
I have a custom naming strategy in a Spring app with Hibernate:
public class MyCustomPhysicalNamingStrategy implements PhysicalNamingStrategy {
#Override
public Identifier toPhysicalTableName(Identifier identifier, JdbcEnvironment jdbcEnvironment) {
return Identifier.toIdentifier("my_custom_table_name");
}
// ...
}
The Spring documentation says I can tell Hibernate to use it either by setting spring.jpa.hibernate.naming.physical-strategy (which works fine), or like this:
Alternatively, if ImplicitNamingStrategy or PhysicalNamingStrategy beans are available in the application context, Hibernate will be automatically configured to use them
I need to use this second method, as I need to pass some info from Spring's context to my naming strategy. However, I can't get it to work.
I'm creating the bean in the following configuration class:
#Configuration
public class PersistenceConfiguration {
#Bean
public PhysicalNamingStrategy physicalNamingStrategy() {
return new MyCustomPhysicalNamingStrategy();
}
}
What am I missing here?
Thanks in advance.
EDIT: I'm on Spring Boot/Spring JPA 1.5.9.RELEASE, which gives me Hibernate core v.5.0.12.Final and Hibernate JPA API 2.1.
It seems that this is only supported with spring boot 2.
See: https://github.com/spring-projects/spring-boot/blob/v2.0.3.RELEASE/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.java#L95
This file is not present in spring boot 1.5: https://github.com/spring-projects/spring-boot/tree/v1.5.14.RELEASE/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa
(HibernateJpaAutoConfiguration.java doesn't have it)
I a trying to find way to have the auditing features of spring-data-mongodb when performing some tests.
My tests are basically starting an embedded mongo, and wiring my db component with a MongoTemplate.
public class MyRepoImpl implements MyRepoCustom {
#Autowired
public MyRepoImpl(MongoOperations operations) {
/* init stuff here */
}
#Override
public MyModel runComplexQuery() { /* do stuff here, such as saving data */ }
}
When I am performing the tests, I can't find a way to set up the MongoTemplate in such a way that the auditing takes place (at least for some specific tests), without loading the whole Spring application.
Is there a way to set the MongoTemplate/MongoOperations in order to have the auditing feature running ?
With Spring Boot you can use the #DataMongoTest annotation to only configure MongoDB starter.
#RunWith(SpringRunner.class)
#DataMongoTest
#Import(ExtraMongoConfiguration.class) // if you need some #Configuration to be imported
public class SomeRepositoryTests {
#Autowired
private MongoTemplate mongoTemplate;
}
I use spring-boot-starter-data-solr and would like to make use of the schmea cration support of Spring Data Solr, as stated in the documentation:
Automatic schema population will inspect your domain types whenever the applications context is refreshed and populate new fields to your index based on the properties configuration. This requires solr to run in Schemaless Mode.
However, I am not able to achieve this. As far as I can see, the Spring Boot starter does not enable the schemaCreationSupport flag on the #EnableSolrRepositories annotation. So what I tried is the following:
#SpringBootApplication
#EnableSolrRepositories(schemaCreationSupport = true)
public class MyApplication {
#Bean
public SolrOperations solrTemplate(SolrClient solr) {
return new SolrTemplate(solr);
}
}
But looking in Wireshark I cannot see any calls to the Solr Schema API when saving new entities through the repository.
Is this intended to work, or what am I missing? I am using Solr 6.2.0 with Spring Boot 1.4.1.
I've run into the same problem. After some debugging, I've found the root cause why the schema creation (or update) is not happening at all:
By using the #EnableSolrRepositories annotation, an Spring extension will add a factory-bean to the context that creates the SolrTemplate that is used in the repositories. This template initialises a SolrPersistentEntitySchemaCreator, which should do the creation/update.
public void afterPropertiesSet() {
if (this.mappingContext == null) {
this.mappingContext = new SimpleSolrMappingContext(
new SolrPersistentEntitySchemaCreator(this.solrClientFactory)
.enable(this.schemaCreationFeatures));
}
// ...
}
Problem is that the flag schemaCreationFeatures (which enables the creator) is set after the factory calls the afterPropertiesSet(), so it's impossible for the creator to do it's work.
I'll create an issue in the spring-data-solr issue tracker. Don't see any workaround right now, other either having a custom fork/build of spring-data or extend a bunch of spring-classes and trying to get the flag set before by using (but doubt of this can be done).