JdbcMetadataStore: initialize schema, but for Spring app NOT using Boot - spring

Versions:
Spring: 5.2.16.RELEASE
Spring Integrations: 5.3.9.RELEASE
PostgreSQL: 13.x
I have implemented a pure Spring 5.x webapp; there is no Spring Boot.
I am using the JdbcMetadataStore and require that the PostgreSQL database be initialized with schema definitions located on the classpath in:
classpath:org/springframework/integration/jdbc/schema-postgresql.sql
Following a very useful article on the topic, here are properties I put in spring.properties:
spring.integration.jdbc.initialize-schema=always
spring.integration.jdbc.schema=classpath:org/springframework/integration/jdbc/schema-postgresql.sql
This DOES NOT work. After researching this issue, I have learned that the start-up initialization is supported, but in Spring Boot.
QUESTION: Short of explicitly handling the execution of this SQL script elsewhere during webapp initialization, is there any standard way to load the script listed above at start-up? NOTE: I am not using schema.sql scripts or similar to initialize my backend.

See DataSourceInitializer in spring-jdbc:
/**
* Used to {#linkplain #setDatabasePopulator set up} a database during
* initialization and {#link #setDatabaseCleaner clean up} a database during
* destruction.
* #see DatabasePopulator
*/
public class DataSourceInitializer implements InitializingBean, DisposableBean {
You need to inject over there a ResourceDatabasePopulator based on that script location:
/**
* Populates, initializes, or cleans up a database using SQL scripts defined in
* external resources.
*
* <ul>
* <li>Call {#link #addScript} to add a single SQL script location.
* <li>Call {#link #addScripts} to add multiple SQL script locations.
* <li>Consult the setter methods in this class for further configuration options.
* <li>Call {#link #populate} or {#link #execute} to initialize or clean up the
* database using the configured scripts.
* </ul>
*
* #see DatabasePopulatorUtils
* #see ScriptUtils
*/
public class ResourceDatabasePopulator implements DatabasePopulator {
Some docs are here: https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#jdbc-initializing-datasource

Related

When using Spring R2DBC repositories can I provide my own custom R2dbcEntityTemplate?

I can make required tweaks via:
#EnableR2dbcRepositories(repositoryBaseClass = BaseRepo::class)
but this is too high level. Essentially I override top level method with custom behaviour which needs to be done at R2dbcEntityTemplate.class level so would be great if I could provide custom R2DBC teamplate. I tried declaring bean but it doesn't pick mine, predictably due to being hardcoded in:
/*
* (non-Javadoc)
* #see org.springframework.data.repository.core.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.core.RepositoryInformation)
*/
#Override
protected Object getTargetRepository(RepositoryInformation information) {
RelationalEntityInformation<?, ?> entityInformation = getEntityInformation(information.getDomainType(),
information);
return getTargetRepositoryViaReflection(information, entityInformation,
new R2dbcEntityTemplate(this.databaseClient, this.dataAccessStrategy), this.converter);
}
Instead of repositoryBaseClass, you can use
#EnableR2dbcRepositories(repositoryFactoryBeanClass = MyR2dbcRepositoryFactoryBean.class)
Then on your MyR2dbcRepositoryFactoryBean you can set your own R2dbcEntityTemplate use the method setEntityOperations

RepositoryConfigurationExtensionSupport => useRepositoryConfiguration => "Reactive Repositories are not supported by %s. Offending repository is %s!"

with org.springframework.boot update from 2.0.4.RELEASE to 2.0.5.RELEASE start to get exception:
RepositoryConfigurationExtensionSupport
=> useRepositoryConfiguration
=> InvalidDataAccessApiUsageException: "Reactive Repositories are not supported by %s. Offending repository is %s!"
what is the best/simplest Spring Data workaround for this? Could someone provide example? if it is possible without redefinition of #EnableJpaRepositories and JpaRepositoriesRegistrar...
This is solution, but I don't like it.. +3 new classes..
EnableXxxJpaRepositories is practically exact copy of EnableJpaRepositories..
EnableXxxJpaRepositories:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
#Import(XxxJpaRepositoriesRegistrar.class)
public #interface EnableXxxJpaRepositories {
/**
* Alias for the {#link #basePackages()} attribute. Allows for more concise annotation
* declarations e.g.: {#code #EnableXxxJpaRepositories("org.my.pkg")} instead of {#code
*
* #EnableXxxJpaRepositories(basePackages="org.my.pkg")}.
*/
String[] value() default {};
/**
* Base packages to scan for annotated components. {#link #value()} is an alias for (and mutually
* exclusive with) this attribute. Use {#link #basePackageClasses()} for a type-safe alternative
* to String-based package names.
*/
String[] basePackages() default {};
/**
* Type-safe alternative to {#link #basePackages()} for specifying the packages to scan for
* annotated components. The package of each class specified will be scanned. Consider creating a
* special no-op marker class or interface in each package that serves no purpose other than being
* referenced by this attribute.
*/
Class<?>[] basePackageClasses() default {};
/**
* Specifies which types are eligible for component scanning. Further narrows the set of candidate
* components from everything in {#link #basePackages()} to everything in the base packages that
* matches the given filter or filters.
*/
Filter[] includeFilters() default {};
/**
* Specifies which types are not eligible for component scanning.
*/
Filter[] excludeFilters() default {};
/**
* Returns the postfix to be used when looking up custom repository implementations. Defaults to
* {#literal Impl}. So for a repository named {#code PersonRepository} the corresponding
* implementation class will be looked up scanning for {#code PersonRepositoryImpl}.
*/
String repositoryImplementationPostfix() default "Impl";
/**
* Configures the location of where to find the Spring Data named queries properties file. Will
* default to {#code META-INF/jpa-named-queries.properties}.
*/
String namedQueriesLocation() default "";
/**
* Returns the key of the {#link QueryLookupStrategy} to be used for lookup queries for query
* methods. Defaults to {#link Key#CREATE_IF_NOT_FOUND}.
*/
Key queryLookupStrategy() default Key.CREATE_IF_NOT_FOUND;
/**
* Returns the {#link FactoryBean} class to be used for each repository instance. Defaults to
* {#link JpaRepositoryFactoryBean}.
*/
Class<?> repositoryFactoryBeanClass() default JpaRepositoryFactoryBean.class;
/**
* Configure the repository base class to be used to create repository proxies for this particular
* configuration.
*
* #since 1.9
*/
Class<?> repositoryBaseClass() default DefaultRepositoryBaseClass.class;
// JPA specific configuration
/**
* Configures the name of the {#link EntityManagerFactory} bean definition to be used to create
* repositories discovered through this annotation. Defaults to {#code entityManagerFactory}.
*/
String entityManagerFactoryRef() default "entityManagerFactory";
/**
* Configures the name of the {#link PlatformTransactionManager} bean definition to be used to
* create repositories discovered through this annotation. Defaults to {#code
* transactionManager}.
*/
String transactionManagerRef() default "transactionManager";
/**
* Configures whether nested repository-interfaces (e.g. defined as inner classes) should be
* discovered by the repositories infrastructure.
*/
boolean considerNestedRepositories() default false;
/**
* Configures whether to enable default transactions for Spring Data JPA repositories. Defaults to
* {#literal true}. If disabled, repositories must be used behind a facade that's configuring
* transactions (e.g. using Spring's annotation driven transaction facilities) or repository
* methods have to be used to demarcate transactions.
*
* #return whether to enable default transactions, defaults to {#literal true}.
*/
boolean enableDefaultTransactions() default true;
}
XxxJpaRepositoriesRegistrar:
#Configuration
public class XxxJpaRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport {
#Override
protected Class<? extends Annotation> getAnnotation() {
return EnableXxxJpaRepositories.class;
}
#Override
protected RepositoryConfigurationExtension getExtension() {
return new XxxJpaRepositoryConfigExtension();
}
}
XxxJpaRepositoryConfigExtension:
#Component
public class XxxJpaRepositoryConfigExtension extends JpaRepositoryConfigExtension {
#Override
protected boolean useRepositoryConfiguration(RepositoryMetadata metadata) {
//return super.useRepositoryConfiguration(metadata);
//return metadata.isReactiveRepository();
return true;
}
}

What are advantages of using #Transactional(readOnly = true)?

I am beginner and as I understand #Transactional simply make sure that all the internal work of a class or method annotated with #Transactional will be wrapped in one transaction and all of the calls from external sources will create a new transaction but why do we actually need these annotations in Repository below and what are advantages of using it with readOnly = true in common cases? This is Spring pet-clinic example application using Spring & Hibernate (https://github.com/spring-projects/spring-petclinic).
/**
* Repository class for <code>Pet</code> domain objects All method names are compliant with Spring Data naming
* conventions so this interface can easily be extended for Spring Data See here: http://static.springsource.org/spring-data/jpa/docs/current/reference/html/jpa.repositories.html#jpa.query-methods.query-creation
*
* #author Ken Krebs
* #author Juergen Hoeller
* #author Sam Brannen
* #author Michael Isvy
*/
public interface PetRepository extends Repository<Pet, Integer> {
/**
* Retrieve all {#link PetType}s from the data store.
* #return a Collection of {#link PetType}s.
*/
#Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name")
#Transactional(readOnly = true)
List<PetType> findPetTypes();
/**
* Retrieve a {#link Pet} from the data store by id.
* #param id the id to search for
* #return the {#link Pet} if found
*/
#Transactional(readOnly = true)
Pet findById(Integer id);
/**
* Save a {#link Pet} to the data store, either inserting or updating it.
* #param pet the {#link Pet} to save
*/
void save(Pet pet);
}
From the explanation of Oliver Gierke - the Spring Data author:
Reading methods like findAll() and findOne(…) are using
#Transactional(readOnly = true) which is not strictly necessary but
triggers a few optimizations in the transaction infrastructure
(setting the FlushMode to MANUAL to let persistence providers
potentially skip dirty checks when closing the EntityManager). Beyond
that the flag is set on the JDBC Connection as well which causes
further optimizations on that level.
Depending on what database you use it can omit table locks or even
reject write operations you might trigger accidentally. Thus we
recommend using #Transactional(readOnly = true) for query methods as
well which you can easily achieve adding that annotation to you
repository interface. Make sure you add a plain #Transactional to the
manipulating methods you might have declared or re-decorated in that
interface.
Further reading:
Spring read-only transaction Hibernate optimization
Read-write and read-only transaction routing with Spring

doInTable(HTableInterface table) is Deprecated

What can I use instead of doInTable(HTableInterface table) which is deprecated.
Below is the code.
hbaseTemplate.execute(tableName, new TableCallback<User>() {
public User doInTable(HTableInterface table) throws Throwable {
Put p = new Put(Bytes.toBytes(KEY),timestamp);
p.addColumn(FAMILY, XXX, Bytes.toBytes(user.getUserId()));
table.put(p);
}
});
I am using Spring Boot with Hbase.
Thank you in advance.
The source code mentioned using org.apache.hadoop.hbase.client.Table instead of HTableInterface.
/**
* Used to communicate with a single HBase table.
* Obtain an instance from an {#link HConnection}.
*
* #since 0.21.0
* #deprecated use {#link org.apache.hadoop.hbase.client.Table} instead
*/
#Deprecated
#InterfaceAudience.Private
#InterfaceStability.Stable
public interface HTableInterface extends Table ...

Spring Boot + Elastic Search

I am trying to setup an application with Spring Boot and Elastic Search. This application already use Spring Data JPA repositories to persist my entities. The problem that I have when I try to run the application with the ElasticSearch configuration enabled is that I am getting an exception when the repositories are scanned.
I am getting the following exception:
Caused by: java.lang.IllegalArgumentException: Unable to obtain mapping metadata for int!
My repository is defined in the following way:
#Repository
public interface AdminUserRepository extends PagingAndSortingRepository<AdminUser, Long> {
/**
* Returns an AdminUser that match the email specified by parameter.
* #param email AdminUser email.
* #return An AdminUser instance.
*/
AdminUser findByEmail(final String email);
/**
* Returns an AdminUser that match the email and business name specified by parameter.
* #param email AdminUser email.
* #param businessName Business Name.
* #return number of matching instances.
*/
int countByEmailAndBusinessName(final String email, final String businessName);
}
Seems that the exception occurs due to the signature of the count query which returns an int. Even though this repository works fine with JPA, it throws an exception enabling elastic search.
I want to know if there is any restrictions with the return type in a repository or if I am missing something in my configuration.
My Java config class:
#Configuration
#EnableElasticsearchRepositories
public class ElasticSearchConfig {
}
Thanks!
It looks like Spring Data Elasticsearch is finding a repository intended for use with Spring Data JPA. When you're using multiple Spring Data modules in the same application, you should place the repositories in separate packages and then reference this package on the #Enable... annotation.
For example:
#Configuration
#EnableElasticsearchRepositories("com.foo.elasticsearch")
#EnableJpaRepositories("com.foo.jpa")
public class MyConfiguration {
}
For counts spring data uses Long not int. Try changing the type of the method that should work.
Here is a reference at the docs: http://docs.spring.io/autorepo/docs/spring-data-elasticsearch/2.0.2.RELEASE/reference/html/#repositories.core-concepts

Resources