Spring Boot multiple configurations files with same names and same properties names - java

I am trying to setup a Spring Boot monolithic app which should behave like a microservice and I have some issues to manage configurations (.properties).
This is how I organized the project (resources folder) :
As you can see there are common properties files : application.properties,application-dev.properties and application-prod.properties these properties should be shared by all sub-properties and they can eventually be overridden.
Each service has its own data source url and it also depending on the active profile : H2 for dev and MySQL for prod.
Here an exemple of how I manage configurations :
Contents of common application.properties:
spring.profiles.active=dev
spring.config.additional-location=classpath:productservice/, classpath:userservice/,classpath:financeservice/, classpath:securityservice/ #I am not sure this works...
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.database=default
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.validation-mode=none
Contents of common application-dev.properties:
spring.datasource.driver-class-name=org.h2.Driver
spring.h2.console.enabled=true
spring.h2.console.path=/db
spring.h2.console.settings.trace=false
spring.h2.console.settings.web-allow-others=false
spring.datasource.username=sa # We override data source username and paswword
spring.datasource.password=
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
Contents of common application-prod.properties:
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
Now for specific services :
productservice :
Content of application.properties:
spring.liquibase.change-log=classpath:productservice/db/changelog/db.changelog-master.xml
spring.liquibase.default-schema=productservicedb
spring.liquibase.check-change-log-location=true
Content of application-dev.properties:
spring.datasource.url=jdbc:h2:mem:productservicedb;DB_CLOSE_ON_EXIT=FALSE;INIT=CREATE SCHEMA IF NOT EXISTS productservicedb;MV_STORE=FALSE;MVCC=FALSE
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
Content of application-prod.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/productservicedb?useSSL=false
And for userservice I have :
Content of application.properties :
spring.liquibase.change-log=classpath:userservice/db/changelog/db.changelog-master.xml
spring.liquibase.default-schema=userservice
spring.liquibase.check-change-log-location=true
Content of application-dev.properties :
spring.datasource.url=jdbc:h2:mem:userservicedb;DB_CLOSE_ON_EXIT=FALSE;INIT=CREATE SCHEMA IF NOT EXISTS userservicedb;MV_STORE=FALSE;MVCC=FALSE
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
Content of application-prod.properties :
spring.datasource.url=jdbc:mysql://localhost:3306/userservicedb
And so on ... for other services.
To configure each data source I proceed like this :
productservice :
1- Retrieve properties from environment :
#Configuration( "productServiceProperties" )
#Getter
#Setter
#PropertySource( value = { "classpath:productservice/application.properties",
"classpath:productservice/application-${spring.profiles.active}.properties" } )
public class ProductServiceProperties {
#Autowired//I want `Environment` to contain properties from common properties + files in #PropertySource above
private Environment environment;
// ========DATASOURCE PROPERTIES=========
private String datasourceDriverClass;
private String datasourceUsername;
private String dataSourcePassword;
private String dataSourceUrl;
// ========LIQUIBASE PROPERTIES==========
private String liquibaseChangeLog;
private String liquibaseDefaultSchema;
#PostConstruct
public void init() {
this.datasourceDriverClass = environment.getProperty( "spring.datasource.driver-class-name" );
datasourceUsername = environment.getProperty( "spring.datasource.username" );
dataSourcePassword = environment.getProperty( "spring.datasource.password" );
dataSourceUrl = environment.getProperty( "spring.datasource.url" );
liquibaseChangeLog = environment.getProperty( "spring.liquibase.change-log" );
liquibaseDefaultSchema = environment.getProperty( "spring.liquibase.default-schema" );
log.debug( "Initialisation {} ", datasourceDriverClass );
}
}
2- Inject them in the DB configuration :
#Configuration
#EnableJpaRepositories( basePackages = "com.company.product.repository", entityManagerFactoryRef = "productServiceEntityManager", transactionManagerRef = "productServiceTransactionManager", considerNestedRepositories = true )
#EnableTransactionManagement( proxyTargetClass = false )
public class ProductServiceDBConfig {
private static final String packageToScan = "com.company.product.models";
#Autowired
private ProductServiceProperties productServiceProperties;
#Bean( name = "productServiceDataSource" )
public DataSource productServiceDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName( productServiceProperties.getDatasourceDriverClass() );
dataSource.setUrl( productServiceProperties.getDataSourceUrl() );
dataSource.setUsername( productServiceProperties.getDatasourceUsername() );
dataSource.setPassword( productServiceProperties.getDataSourcePassword() );
return dataSource;
}
#Bean
public JpaVendorAdapter productServiceVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setGenerateDdl( false );
hibernateJpaVendorAdapter.setShowSql( true );
return hibernateJpaVendorAdapter;
}
#Bean( name = "productServiceEntityManager" )
public LocalContainerEntityManagerFactoryBean productServiceEntityManager() {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource( productServiceDataSource() );
emf.setPackagesToScan( packageToScan );
emf.setJpaVendorAdapter( productServiceVendorAdapter() );
return emf;
}
#Bean( name = "productServiceTransactionManager" )
public PlatformTransactionManager productServiceTransactionManager() {
JpaTransactionManager productTransactionManager = new JpaTransactionManager();
productTransactionManager.setEntityManagerFactory( productServiceEntityManager().getObject() );
return productTransactionManager;
}
#Bean
public SpringLiquibase productServiceLiquibase() {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource( productServiceDataSource() );
liquibase.setChangeLog( productServiceProperties.getLiquibaseChangeLog() );
liquibase.setDefaultSchema( productServiceProperties.getLiquibaseDefaultSchema() );
return liquibase;
}
}
userservice :
1-
#Configuration( "userServiceProperties" )
#Getter
#Setter
#PropertySource( value = { "classpath:userservice/application.properties",
"classpath:userservice/application-${spring.profiles.active}.properties" } ) //I want properties from properties file source above
public class UserServiceProperties {
#Autowired //I want `Environment` to contain properties from common properties + files in #PropertySource above
private Environment environment;
// ========DATASOURCE PROPERTIES=========
private String datasourceDriverClass;
private String datasourceUsername;
private String dataSourcePassword;
private String dataSourceUrl;
// ========LIQUIBASE PROPERTIES==========
private String liquibaseChangeLog;
private String liquibaseDefaultSchema;
#PostConstruct
public void init() {
datasourceDriverClass = environment.getProperty( "spring.datasource.driver-class-name" );
datasourceUsername = environment.getProperty( "spring.datasource.username" );
dataSourcePassword = environment.getProperty( "spring.datasource.password" );
dataSourceUrl = environment.getProperty( "spring.datasource.url" );
liquibaseChangeLog = environment.getProperty( "spring.liquibase.change-log" );
liquibaseDefaultSchema = environment.getProperty( "spring.liquibase.default-schema" );
}
}
2-
#Configuration
#EnableJpaRepositories( basePackages = "com.company.user.repository", entityManagerFactoryRef = "userServiceEntityManager", transactionManagerRef = "userServiceTransactionManager", considerNestedRepositories = true )
#EnableTransactionManagement( proxyTargetClass = false )
public class UserServiceDBConfig {
private static final String packageToScan = "com.company.user.models";
#Autowired
private UserServiceProperties userServiceProperties;
#Bean( name = "userServiceDataSource" )
public DataSource userServiceDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName( userServiceProperties.getDatasourceDriverClass() );
dataSource.setUrl( userServiceProperties.getDataSourceUrl() );
dataSource.setUsername( userServiceProperties.getDatasourceUsername() );
dataSource.setPassword( userServiceProperties.getDataSourcePassword() );
return dataSource;
}
#Bean
public JpaVendorAdapter userServiceVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setGenerateDdl( false );
hibernateJpaVendorAdapter.setShowSql( true );
return hibernateJpaVendorAdapter;
}
#Bean( name = "userServiceEntityManager" )
public LocalContainerEntityManagerFactoryBean userServiceEntityManager() {
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setDataSource( userServiceDataSource() );
emf.setPackagesToScan( packageToScan );
emf.setJpaVendorAdapter( userServiceVendorAdapter() );
return emf;
}
#Bean( name = "userServiceTransactionManager" )
public PlatformTransactionManager userServiceTransactionManager() {
JpaTransactionManager userTransactionManager = new JpaTransactionManager();
userTransactionManager.setEntityManagerFactory( userServiceEntityManager().getObject() );
return userTransactionManager;
}
#Bean
public SpringLiquibase userServiceLiquibase() {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource( userServiceDataSource() );
liquibase.setChangeLog( userServiceProperties.getLiquibaseChangeLog() );
liquibase.setDefaultSchema( userServiceProperties.getLiquibaseDefaultSchema() );
return liquibase;
}
}
The problem is, as I #Autowired Environment to get my properties and all properties have same names, some properties always take precedence over other properties. E.g. the property spring.datasource.url contains the same value in both services.
I use Environment and not #Value neither #ConfigurationProperties because it gets properties basing on the active profile and that exactly what I want.
My questions are :
Is it possible to configure Environment to get properties from the configuration class where it is injected?
If not, is there a simple way to achieve what I want to do?
Should I configure multiple application contexts (one per service) to isolate each Environment variable from other? (I am not sure this would work)
Thanks a lot (and sorry for the length of this post)

I solve the problem myself with the suggestion of Andy Wilkinson (#ankinson) on Twitter.
We can't have many properties files with same name. So I had to rename my sub configurations files like this :
product.properties
product-dev.properties
product-prod.properties
And so on...

Related

JPA: 2 databases: one "update", the other "create"

I want to use two database schemas: one with static data (which cannot be changed), the other with dynamic data (which will change constantly during the execution of the Spring application).
In application.properties I have prescribed the following:
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/XXXXXX
spring.datasource.username=root
spring.datasource.password=root
spring.second-datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/YYYYYY
spring.second-datasource.username=root
spring.second-datasource.password=root
But what should I do now with the spring.jpa.hibernate.ddl-auto parameter?
For one scheme I want to install:
spring.jpa.hibernate.ddl-auto=create
And for the other (where the data is always static):
spring.jpa.hibernate.ddl-auto=update
or
spring.jpa.hibernate.ddl-auto=none
You have an issue that you need two different datasources. You should define them as 2 different beans and autowire them seperately. For clarity it's advisable to also use 2 different properties files.
You can create a Configuration class and use specific properties file to populate fields like so:
#Configuration
#PropertySource("classpath:db.properties")
public class Config {
#Value("${db.driverClassName}")
String driverClassName;
#Value("${db.url}")
String url;
#Value("${db.username}")
String username;
#Value("${db.password}")
String password;
#Bean("datasourceId")
public DataSource dataSource() {
var dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(
{ "com.foo.bar" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
private final Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty(
"hibernate.hbm2ddl.auto", "create-drop");
return hibernateProperties;
}
}

SpringBoot: Configuring Spring DataSource for Tests

I have a SpringBoot app.
I have created this test:
#ContextConfiguration(classes={TestConfig.class})
#RunWith(SpringRunner.class)
#SpringBootTest
public class SuncionServiceITTest {
#Test
public void should_Find_2() {
// TODO
}
}
where
#Configuration
#EnableJpaRepositories(basePackages = "com.plats.bruts.repository")
#PropertySource("local-configuration.properties")
#EnableTransactionManagement
#SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class TestConfig {
}
and local configuration.properties:
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
but when I run the test. I got this error:
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'entityManagerFactory' available
I also tried with:
#EnableJpaRepositories(basePackages = "com.plats.bruts.repository", entityManagerFactoryRef="emf")
but then I have the error:
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'emf' available
Looks like you are missing below starter dependency. This starter dependency has all the necessary dependencies needed to configure the jpa repositories.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
This is an approach of how to configure several data sources in one application. I have tested it for spring-webmvc and graphql-java, but I think it can be useful for spring-boot as well.
Configure several data sources with spring-data-jpa
For each database we should enable JPA repositories and specify the base packages for the corresponding interfaces. Also for each database we should specify the entity manager factory and the base packages for corresponding entities, as well as the transaction manager and the data source, of course.
To do this, we'll include in our application one configuration class for data JPA and three inner classes for each database. See the Simple GraphQL implementation.
DataJpaConfig.java
package org.drakonoved.graphql;
#Configuration
#PropertySource(value = "classpath:resources/application.properties", encoding = "UTF-8")
public class DataJpaConfig {
private final String basePackage = "org.drakonoved.graphql";
#EnableJpaRepositories(
basePackages = basePackage + ".repository.usersdb",
entityManagerFactoryRef = "usersdbEntityManagerFactory",
transactionManagerRef = "usersdbTransactionManager")
public class UsersDBJpaConfig {
#Bean("usersdbEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean usersDBEntityManagerFactoryBean(
#Value("${datasource.usersdb.script}") String script) {
return createEntityManagerFactoryBean(
script, "usersdb", basePackage + ".dto.usersdb");
}
#Bean("usersdbTransactionManager")
public PlatformTransactionManager usersDBTransactionManager(
#Qualifier("usersdbEntityManagerFactory")
LocalContainerEntityManagerFactoryBean factoryBean) {
return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
}
}
#EnableJpaRepositories(
basePackages = basePackage + ".repository.rolesdb",
entityManagerFactoryRef = "rolesdbEntityManagerFactory",
transactionManagerRef = "rolesdbTransactionManager")
public class RolesDBJpaConfig {
#Bean("rolesdbEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean rolesDBEntityManagerFactoryBean(
#Value("${datasource.rolesdb.script}") String script) {
return createEntityManagerFactoryBean(
script, "rolesdb", basePackage + ".dto.rolesdb");
}
#Bean("rolesdbTransactionManager")
public PlatformTransactionManager rolesDBTransactionManager(
#Qualifier("rolesdbEntityManagerFactory")
LocalContainerEntityManagerFactoryBean factoryBean) {
return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
}
}
#EnableJpaRepositories(
basePackages = basePackage + ".repository.usersnrolesdb",
entityManagerFactoryRef = "usersnrolesdbEntityManagerFactory",
transactionManagerRef = "usersnrolesdbTransactionManager")
public class UsersNRolesDBJpaConfig {
#Bean("usersnrolesdbEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean usersNRolesDBEntityManagerFactoryBean(
#Value("${datasource.usersnrolesdb.script}") String script) {
return createEntityManagerFactoryBean(
script, "usersnrolesdb", basePackage + ".dto.usersnrolesdb");
}
#Bean("usersnrolesdbTransactionManager")
public PlatformTransactionManager usersNRolesDBTransactionManager(
#Qualifier("usersnrolesdbEntityManagerFactory")
LocalContainerEntityManagerFactoryBean factoryBean) {
return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
}
}
//////// //////// //////// //////// //////// //////// //////// ////////
private LocalContainerEntityManagerFactoryBean createEntityManagerFactoryBean(
String script, String dbname, String packagesToScan) {
var factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.setName(dbname)
.addScript(script)
.build());
factoryBean.setPersistenceUnitName(dbname + "EntityManagerFactory");
factoryBean.setPackagesToScan(packagesToScan);
factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
var properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", "none");
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
factoryBean.setJpaPropertyMap(properties);
return factoryBean;
}
}
I prefer to use following approach (i don't like to create own bean configurator). As #svr correctly noticed (see previous answer) you don't add starter package for beans auto configure.
I usually create different profiles: for local app running, for dev, prod and finally last one for tests. In my test profile (application-functests.yml) i configure all settings that needs for my functional tests (datasources, hibernate, cache and so on), i.e. my application-functests.yml of one of my projects:
spring:
http:
encoding:
charset: UTF-8
enabled: true
profiles: functests
jpa:
show-sql: true
properties:
hibernate:
format_sql: true
enable_lazy_load_no_trans: true
naming:
physical-strategy: com.goodt.drive.orgstructure.application.utils.SnakePhysicalNamingStrategy
hibernate:
ddl-auto: none
database-platform: org.hibernate.dialect.PostgreSQL9Dialect
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/monitor_service_functests
username: developer
password: 123
sql-script-encoding: UTF-8
liquibase:
change-log: classpath:db/changelog/changelog.xml
I have only specify profile for running test, therefore all of my functional tests are using functests profile, i.e.:
#SpringBootTest
#ActiveProfiles("functests")
public class TestEventRepository extends FunctionalTestBase {
#Test
public void testGetAll() {
Iterable<EventEntity> eventIterable = dbContext.getEventDataSource().findAll();
Iterator<EventEntity> it = eventIterable.iterator();
List<EventEntity> actualEvents = new ArrayList<>();
while (it.hasNext()) {
actualEvents.add(it.next());
}
List<EventCheckData> expectedEvents = new ArrayList<>() {{
add(new EventCheckData(1L, 1L, "body 1", 1L, 1L));
add(new EventCheckData(2L, 2L, "body 2", 2L, 2L));
add(new EventCheckData(3L, 3L, "body 3", 3L, 1L));
add(new EventCheckData(4L, 1L, "body 4", 2L, 1L));
add(new EventCheckData(5L, 2L, "body 5", 1L, 2L));
}};
EventSimpleChecker.check(expectedEvents, actualEvents);
}
}
In my code example i have base test class - FunctionalTestBase which is used for interact with liquibase (toggle it to apply migrations)

Jedis SpringBoot cannot serialize and deserialize Map<String, Object>

I got following error after adding #Cacheable annotation to one of my rest method:
"status": 500,
"error": "Internal Server Error",
"message": "class java.util.ArrayList cannot be cast to class java.util.Map (java.util.ArrayList and java.util.Map are in module java.base of loader 'bootstrap')",
Method declaration is:
#Cacheable("loadDevicesFloors")
#GetMapping("/floors/all-devices")
public Map<String, DevicesFloorDTO> loadDevicesFloors() {...
and DevicesFloorDTO looks as follows:
public class DevicesFloorDTO implements Serializable {
private final List<DeviceDTO> deviceDTOs;
private final String floorName;
private final Integer floorIndex;
public DevicesFloorDTO(List<DeviceDTO> devicesDtos, String floorName, Integer floorIndex) {
this.deviceDTOs = devicesDtos;
this.floorName = floorName;
this.floorIndex = floorIndex;
}...
Additionally my #Bean redisTemplate method implementation:
#Bean
JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory jedisConFactory
= new JedisConnectionFactory();
jedisConFactory.setHostName(redisHost);
jedisConFactory.setPort(redisPort);
jedisConFactory.setPassword(redisPassword);
return jedisConFactory;
}
#Bean
public RedisTemplate<?, ?> redisTemplate() {
RedisTemplate<byte[], byte[]> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
Does anyone know what is wrong in this implementation? Without #Cacheable it works as expected, but after adding #Cacheable error occurs. I was searching a lot and still don't know what causes this error and how to fix this. Any comment may be helpful. Thaks a lot!
The Generics you have specified for the Map Map<String, DevicesFloorDTO> will not be available at runtime during serialization/deserialization. What format are you trying to save your objects to in Reids? Are they saving as JSON (string) or binary?
We have had success with the GenericJackson2JsonRedisSerializer because it will save the class info inside the JSON string so Redis know exactly how to recreate objects.
There are also some instances where a Wrapper Object is needed in order to correctly serialize/deserialize objects.
#Bean
public RedisCacheManager cacheManager( RedisConnectionFactory redisConnectionFactory,
ResourceLoader resourceLoader ) {
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
.builder( redisConnectionFactory )
.cacheDefaults( determineConfiguration() );
List<String> cacheNames = this.cacheProperties.getCacheNames();
if ( !cacheNames.isEmpty() ) {
builder.initialCacheNames( new LinkedHashSet<>( cacheNames ) );
}
return builder.build();
}
private RedisCacheConfiguration determineConfiguration() {
if ( this.redisCacheConfiguration != null ) {
return this.redisCacheConfiguration;
}
CacheProperties.Redis redisProperties = this.cacheProperties.getRedis();
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
ObjectMapper mapper = new Jackson2ObjectMapperBuilder()
.modulesToInstall( new SimpleModule().addSerializer( new NullValueSerializer( null ) ) )
.failOnEmptyBeans( false )
.build();
mapper.enableDefaultTyping( ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY );
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer( mapper );
//get the mapper b/c they registered some internal modules
config = config.serializeValuesWith( RedisSerializationContext.SerializationPair.fromSerializer( serializer ) );
if ( redisProperties.getTimeToLive() != null ) {
config = config.entryTtl( redisProperties.getTimeToLive() );
}
if ( redisProperties.getKeyPrefix() != null ) {
config = config.prefixKeysWith( redisProperties.getKeyPrefix() );
}
if ( !redisProperties.isCacheNullValues() ) {
config = config.disableCachingNullValues();
}
if ( !redisProperties.isUseKeyPrefix() ) {
config = config.disableKeyPrefix();
config = config.computePrefixWith( cacheName -> cacheName + "::" );
}
return config;
}

JPA with Spring Embebed Database not records found

Starting from a program that was connected to a mysql database, using spring, JPA and JDBC, I am trying to configure that application to use the H2 database in embedded mode.
With MYSQL everything works fine, but with H2 not.
I can not get H2 to return any records, although the records are there so if I do the same query through JDBC, if I see them.
The configuration that I have is the following:
#Configuration
#EnableJpaRepositories("yages.yagesserver")
#EnableTransactionManagement
public class JpaConfig {
#Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder
.setType(EmbeddedDatabaseType.H2)
.setName("yagesh2")
.ignoreFailedDrops(true)
.addScript("db/sql/create-db.sql")
.addScript("db/sql/insert-data.sql")
.generateUniqueName(false)
.build();
return db;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) throws NamingException {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[]{"yages.yagesserver", "yages.yagesserver.dao"});
em.setPersistenceUnitName("yages-server");
em.setJpaVendorAdapter(jpaVendorAdapter());
em.afterPropertiesSet();
return em;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(false);
jpaVendorAdapter.setDatabase(Database.H2);
jpaVendorAdapter.setShowSql(true);
return jpaVendorAdapter;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
}
So if I write this code:
String s="SELECT cal_ano,cal_mes,cal_fecini,cal_fecfin from calendario where cal_ano=? and cal_mes = ?";
List<Calendario> cal =
jdbc.query(s, new Object[] { ano,mes},
(rs, rowNum) -> new Calendario(
rs.getInt("cal_ano"),rs.getInt("cal_mes"),rs.getDate("cal_fecini"),rs.getDate("cal_fecfin"))
);
System.out.println("getDatosSemana. Size "+cal.size()+ "Fecha Inicio: "+cal.get(0).getFechaInicio());
Optional<Calendario> calOpc = calendarioRepositorio.getCalendario(new CalendarioKey(ano - 1, mes));
System.out.println("getDatosSemana. Optional is present: "+calOpc.isPresent());
When I use JDBC I see that in the calendar table if the record exists, but when using JPA, it does not seem to find anything.
This is the output in my console:
getDatosSemana. Size 1Fecha Inicio: 2018-01-28
Hibernate: select calendario0_.cal_ano as cal_ano1_0_0_, calendario0_.cal_mes as cal_mes2_0_0_, calendario0_.cal_fecfin as cal_fecf3_0_0_, calendario0_.cal_fecini as cal_feci4_0_0_ from calendario calendario0_ where calendario0_.cal_ano=? and calendario0_.cal_mes=?
getDatosSemana. Optional is present: false
Of course I have the DAO classes and my repository that extends from a CrudRepository.
Any suggestions, please?
I'm sorry. I have found the error.
It was a failure foolishness. I called the JPA function with the parameters of year and month reversed.
Sometimes you do not see the obvious.

New to HSQLDB working with Spring Application with JavaConfig

I am New to HSQLDB and working with Spring Application with JavaConfig. I want my example to setup a in memory database(HSQLDB) and insert one row.
I think I have my stuff all in order but I don't know where to create the database and the tables.
Below is my main.app code
public class MainApp
{
private static final Logger LOGGER = getLogger(MainApp.class);
#Autowired
protected MessageService mService;
public static void main(String[] args)
{
ApplicationContext context = new AnnotationConfigApplicationContext(HelloWorldConfig.class);
HelloWorld helloWorld = context.getBean(HelloWorld.class);
LOGGER.debug("Message from HelloWorld Bean: " + helloWorld.getMessage());
/**
* I removed the following line... we are now using log4j
*/
//System.out.println(helloWorld.getMessage());
helloWorld.setMessage("I am in Staten Island, New York");
/**
* I removed the following line... we are now using log4j
*/
//System.out.println(helloWorld.getMessage());
LOGGER.debug("Message from HelloWorld Bean: " + helloWorld.getMessage());
}
and here is my DatabaseConfig.class
public class DatabaseConfig
{
private static final Logger LOGGER = getLogger(DatabaseConfig.class);
#Autowired
Environment env;
#Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.HSQL).build();
}
#Bean
public SessionFactory sessionFactory()
{
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setHibernateProperties(getHibernateProperties());
factoryBean.setPackagesToScan(new String[]{"com.xxxx.model"});
try
{
factoryBean.afterPropertiesSet();
} catch (IOException e)
{
LOGGER.error(e.getMessage());
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
return factoryBean.getObject();
}
#Bean
public Properties getHibernateProperties()
{
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
hibernateProperties.setProperty("hibernate.use_sql_comments", env.getProperty("hibernate.use_sql_comments"));
hibernateProperties.setProperty("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.generate_statistics", env.getProperty("hibernate.generate_statistics"));
hibernateProperties.setProperty("javax.persistence.validation.mode", env.getProperty("javax.persistence.validation.mode"));
//Audit History flags
hibernateProperties.setProperty("org.hibernate.envers.store_data_at_delete", env.getProperty("org.hibernate.envers.store_data_at_delete"));
hibernateProperties.setProperty("org.hibernate.envers.global_with_modified_flag", env.getProperty("org.hibernate.envers.global_with_modified_flag"));
return hibernateProperties;
}
#Bean
public HibernateTransactionManager hibernateTransactionManager()
{
HibernateTransactionManager htm = new HibernateTransactionManager();
htm.setSessionFactory(sessionFactory());
htm.afterPropertiesSet();
return htm;
}
but I don't know where the database is create and how do I insert my table before my project runs?
You can download the source code at the following:
https://github.com/JohnathanMarkSmith/HelloSpringJavaBasedJavaConfig/issues/1
This has been asked before, take a look at Startup script to create a schema in HSQLDB

Categories