I have a Maven project that represents the data layer of a single sign on (SSO) server. Other client applications shall rely on it for user authentication and authorization.
It is a .jar
archive that shall be used by another REST
project, also a Maven project.
Both are Spring Boot 2 based projects.
This project shall have some integration tests against the H2 and the MySQL databases.
As it is a library it does not run on its own. But it has some integration tests, that are to be executed. There is therefore no main
class.
The command being used to build and execute the tests is mvn clean install -Denv="test"
My tree of source code looks like:
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── thalasoft
│ │ └── userdata
│ │ ├── config
│ │ │ ├── DatabaseConfiguration.java
│ │ │ ├── JpaService.java
│ │ │ └── properties
│ │ │ ├── AbstractDatabaseProperties.java
│ │ │ ├── DatabaseH2TestProperties.java
│ │ │ ├── DatabaseMySQLAcceptanceProperties.java
│ │ │ ├── DatabaseMySQLPreProdProperties.java
│ │ │ ├── DatabaseMySQLProdProperties.java
│ │ │ ├── DatabaseMySQLTestProperties.java
│ │ │ ├── DatabaseOraclePreProdProperties.java
│ │ │ ├── DatabaseOracleProdProperties.java
│ │ │ ├── DatabaseOracleTestProperties.java
│ │ │ ├── DatabaseProperties.java
│ │ │ └── PropertyNames.java
│ │ ├── dialect
│ │ │ ├── CustomMySQL5InnoDBDialect.java
│ │ │ └── CustomOracle10gDialect.java
│ │ ├── exception
│ │ │ ├── CannotDeleteEntityException.java
│ │ │ ├── EnrichableException.java
│ │ │ ├── EntityAlreadyExistsException.java
│ │ │ ├── EntityNotFoundException.java
│ │ │ └── NoEntitiesFoundException.java
│ │ ├── jpa
│ │ │ ├── domain
│ │ │ │ ├── AbstractEntity.java
│ │ │ │ ├── EmailAddress.java
│ │ │ │ ├── User.java
│ │ │ │ └── UserRole.java
│ │ │ └── repository
│ │ │ ├── GenericRepositoryImpl.java
│ │ │ ├── GenericRepository.java
│ │ │ ├── UserRepositoryCustom.java
│ │ │ ├── UserRepositoryImpl.java
│ │ │ ├── UserRepository.java
│ │ │ ├── UserRoleRepositoryCustom.java
│ │ │ ├── UserRoleRepositoryImpl.java
│ │ │ └── UserRoleRepository.java
│ │ └── service
│ │ ├── UserRoleServiceImpl.java
│ │ ├── UserRoleService.java
│ │ ├── UserServiceImpl.java
│ │ └── UserService.java
│ └── resources
│ ├── application.properties
│ └── custom
│ └── typedef.hbm.xml
└── test
├── java
│ └── com
│ └── thalasoft
│ └── userdata
│ ├── assertion
│ │ └── UserAssert.java
│ ├── it
│ │ ├── jpa
│ │ │ ├── AbstractRepositoryTest.java
│ │ │ ├── UserRepositoryTest.java
│ │ │ └── UserRoleRepositoryTest.java
│ │ └── service
│ │ ├── AbstractServiceTest.java
│ │ └── UserServiceTest.java
│ └── ut
│ ├── AbstractRepositoryTest.java
│ └── UserRepositoryTest.java
└── resources
├── h2
│ └── data-source-test.properties
├── mysql
│ ├── clean-up-before-each-test.sql
│ └── data-source-test.properties
└── oracle
├── data-source-preprod.properties
└── data-source-test.properties
The DatabaseH2TestProperties
is loaded thanks to a custom annotation define in another Maven toolbox project:
@EnvTest
@DbH2
@Configuration
@PropertySource({ "classpath:h2/data-source-test.properties" })
public class DatabaseH2TestProperties extends AbstractDatabaseProperties {
private static Logger logger = LoggerFactory.getLogger(DatabaseH2TestProperties.class);
public DatabaseH2TestProperties() {
logger.debug("===========>> Loading the classpath h2/data-source-test.properties file");
}
}
The data-source-test.properties
file contains:
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.datasource.driver-class-name=net.sf.log4jdbc.DriverSpy
spring.datasource.url=jdbc:log4jdbc:h2:file:./target/useraccounttest
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.show-sql=true
The tests all are based on the class:
@ContextConfiguration(classes = { DatabaseConfiguration.class })
@RunWith(SpringRunner.class)
@Sql(executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, scripts = { "classpath:mysql/clean-up-before-each-test.sql" })
public abstract class AbstractRepositoryTest {
}
On this class, I wonder if I should not use @SpringBootTest(classes = { DatabaseConfiguration.class })
instead.
And the configuration is done with:
@EnableAutoConfiguration
@ComponentScan(nameGenerator = PackageBeanNameGenerator.class, basePackages = { "com.thalasoft.userdata" })
public class DatabaseConfiguration {
}
The connection to the H2 database is successful:
02:23:56.299 [main] DEBUG jdbc.audit - 100. Connection.getMetaData() returned dbMeta74: conn99: url=jdbc:h2:file:./target/useraccounttest user=SA com.zaxxer.hikari.pool.ProxyConnection.getMetaData(ProxyConnection.java:361)
02:23:56.299 [main] DEBUG org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator - Database ->
name : H2
version : 1.4.197 (2018-03-18)
major : 1
minor : 4
02:23:56.299 [main] DEBUG org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator - Driver ->
name : H2 JDBC Driver
version : 1.4.197 (2018-03-18)
major : 1
minor : 4
02:23:56.299 [main] DEBUG org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator - JDBC version : 4.0
02:23:56.299 [main] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
02:23:56.305 [main] DEBUG jdbc.audit - 100. Connection.clearWarnings() returned com.zaxxer.hikari.pool.ProxyConnection.close(ProxyConnection.java:250)
But I get an excetpion.
What the console log has to say:
02:23:56.338 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
02:23:56.338 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'com.thalasoft.userdata.config.JpaService'
02:23:56.338 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'com.thalasoft.userdata.config.properties.DatabaseH2TestProperties'
02:23:56.338 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'com.thalasoft.userdata.jpa.repository.GenericRepositoryImpl'
02:23:56.338 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'com.thalasoft.userdata.jpa.repository.GenericRepositoryImpl'
02:23:56.338 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
02:23:56.340 [main] WARN org.springframework.context.support.GenericApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.thalasoft.userdata.jpa.repository.GenericRepositoryImpl' defined in file [/home/stephane/dev/java/projects/user-data/target/classes/com/thalasoft/userdata/jpa/repository/GenericRepositoryImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.Class<?>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Indeed, I have a custom generic repository:
@Repository
@Transactional
public class GenericRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
implements GenericRepository<T, ID> {
private EntityManager entityManager;
private final Class<T> domainClass;
public GenericRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
super(domainClass, entityManager);
this.entityManager = entityManager;
this.domainClass = domainClass;
}
public EntityManager getEntityManager() {
return entityManager;
}
}
@NoRepositoryBean
public interface GenericRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
public EntityManager getEntityManager();
}
And it is used as:
public interface UserRepository extends GenericRepository<User, Long>, UserRepositoryCustom {
}
public interface UserRepositoryCustom {
public User deleteByUserId(Long id) throws EntityNotFoundException;
}
public class UserRepositoryImpl implements UserRepositoryCustom {
@Autowired
private UserRepository userRepository;
}
It feels like the domain class cannot be found for the generic type.