why is spring boot's DataJpaTest scanning #Component - spring

Confident this hasn't been asked but reading through the Spring docs and testing utilities I found this annotation and thought I'd start using it. Reading through the fine print I read:
Regular #Component beans will not be loaded into the ApplicationContext.
That sounded good and I even liked the idea of using H2 except from what I found the entity I wanted to use had catalog and schema modifiers to it and the default H2 I couldn't figure out how to support that. I made an H2 datasource for the test branch and use that and override the replace. I wound up with
#RunWith(SpringRunner.class)
#ContextConfiguration(classes=ABCH2Congfiguration.class)
#DataJpaTest
#AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
public class StatusRepositoryTest {
}
However my tests fails fro Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type.
which leads to:
Error creating bean with name 'customerServiceImpl': Unsatisfied dependency.
However the customerServiceImpl is this bean:
#Component
public class CustomerServiceImpl implements CustomerService {
}
That says #Component. The fine print for DataJpaTest says it doesn't load #Components. Why is it doing that and thus failing the test?
As Kyle and Eugene asked below here's the rest:
package com.xxx.abc.triage;
#Component
public interface CustomerService {
}
Configuration
#ComponentScan("com.xxx.abc")
#EnableJpaRepositories("com.xxx.abc")
//#Profile("h2")
public class ABMH2Congfiguration {
#Primary
#Bean(name = "h2source")
public DataSource dataSource() {
EmbeddedDatabase build = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).setName("ABC").addScript("init.sql").build();
return build;
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter bean = new HibernateJpaVendorAdapter();
bean.setDatabase(Database.H2);
bean.setShowSql(true);
bean.setGenerateDdl(true);
return bean;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
bean.setDataSource(dataSource);
bean.setJpaVendorAdapter(jpaVendorAdapter);
bean.setPackagesToScan("com.xxx.abc");
return bean;
}
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
And just to clarify the question, why is #Component being loaded into the context within a #DataJpaTest?

#ComponentScan automatically inject all found #Component and #Service into context. You could override it by separate #Bean:
#Bean
CustomerService customerService{
return null;
}
Or remove #Component annotation from CustomerService and CustomerServiceImpl, but you should add #Bean at your production #Configuration

#DataJpaTest does not load #Component, #Service... by default, only #Repository and internal things needed to configure Spring data JPA.
In your test, you can load any #Configuration you need, and in your case, you load #ABMH2Congfiguration which performs a #ComponentScan that's why Spring try to load your CustomerService.
You should only scanning the #Repository in this configuration class, and scan others #Component, #Service... in another #Configuration like DomainConfiguration. It's always a good practice to separate different types of configurations.

Related

Spring Boot How to load only specified #Repository components

I have a project containing many Dao annotated by #Repository each.
Also several spring boot projects, each having its spring context an can be run independently and they have a reference to the project containing the Daos.
The thing is, I don't want to load all Dao into the spring context in each project. Only some specified Dao are required for each spring boot project.
I used to specify Dao classes by defining them as beans in an XML configuration for each project.
Now we are moving to java and annotation based configuration.
Is there a way to tell the spring context only to load the #Repository that I specify?
I know I can make a #Configuration class and define #Bean methods but I still need them to be treated as #Repository and not a normal bean. Any idea if this is supported and how to implement this?
You can use #Conditional on each of those DAO classes.
Class will be loaded in context only when the condition mentioned using #Conditional annotation is fulfilled. You can have condition like:
#ConditionalOnProperty(
value="module.name",
havingValue = "module1",
matchIfMissing = false)
class DaoForModule1 {
This will load the DaoModule1 if and only if the property module.name has value module1. If you want to load this DaoModule1 when proerty is not set, you can change matchIfMissing to true.
You can also use #Profile annotation to limit the classes loaded based on profile
#Profile("module2")
class DaoForModule2 {
This would load DaoForModule2 only when you have module2 in the list of active profiles. But i would not prefer profile as the use case of profiles is different. We use profiles generally to specify variable resources based on environment.
#SpringBootApplication just combine #EnableAutoConfiguration , #SpringBootConfiguration and #ComponentScan.
The #ComponentScan is the guy that cause all #Repository beans under the scanned package to be registered automatically which is the thing that you don't want it to happen.
So you can use these annotations separately but excluding #ComponentScan. And use #Import to explicitly define the beans that you want to register.
The main application class will look like :
#SpringBootConfiguration
#EnableAutoConfiguration
#Import(value = {FooRepoistory.class, BarRepository.class,.......})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
From your question, I assume you want to reuse a Spring DAO project with multiple repositories and JPA Entity objects, maybe belonging to different datasources, in several other Spring projects. You prefer to load only a specific set of the JPA entities/repos. The first step is to organize the related entities and repositories into distinct packages and include this project in the path of the other projects.
This is one way to handle this, assuming you have separated the repositories and entities into different packages. Create your own Configuration bean that will instantiate a JPA EntityManagerFactory bean with the specific packages and datasource it needs. in this code below, EntityManagerFactory below will load the entities from MODEL_PACKAGE and the repositories from REPOSITORIES_PACKAGE.
#Configuration
#ComponentScan(basePackages = MODEL_PACKAGE)
#EnableJpaRepositories(basePackages = REPOSITORIES_PACKAGE,
entityManagerFactoryRef = "ENTITY_MANAGER_FACTORY")
#EnableTransactionManagement
public class PersistenceConfig {
public static final String MODEL_PACKAGE = "Your model package";
public static final String REPOSITORIES_PACKAGE = "Your repository package";
public static final String ENTITY_MANAGER_FACTORY = "entity_manager_factory";
public static final String TRANSACTION_MANAGER = "transaction_manager";
#Autowired //This is to get your property file entries (DB connection, etc).
private Environment environment;
#Bean(DATA_SOURCE)
public DataSource dataSource() {
//Create your datasource from environment properties. Example - org.apache.tomcat.jdbc.pool.DataSource
}
#Bean(ENTITY_MANAGER_FACTORY) #Autowired
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
#Qualifier(DATA_SOURCE) DataSource dataSource) throws IllegalStateException {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
Properties jpaProperties = new Properties();
// set properties for your JPA, for example, hibernate.dialect, hibernate.format_sql, etc.
entityManagerFactoryBean.setJpaProperties(jpaProperties);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan(MODEL_PACKAGE);
}
#Bean(TRANSACTION_MANAGER) #Autowired
#Primary
#Qualifier(value = "transactionManager")
public JpaTransactionManager transactionManager(
#Qualifier(ENTITY_MANAGER_FACTORY) EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;

Spring - Data persisted by JdbcTemplate unable to be seen by JpaRepository

I am building a Spring Boot application which requires the need for persistence via JDBC and selecting/reading via JPA/Hibernate. I have implemented both of these types of operations using Spring's JdbcTemplate and Spring Data's JpaRepository.
After I persist using JdbcTemplate I am unable to see the data via JpaRepository even though they share the same datasource. I am able to read the data if I use JdbcTemplate.
NOTE: I am using two data sources. One is configured in another class without the #Primary annotation using its own entity manager factory and transaction manager, which is why I've needed to explicitly define it below using Spring Boot's default bean terminology "transactionManager" and "entityManagerFactory".
The following is my embedded database configuration for the primary beans:
#Configuration
#EnableJpaRepositories(basePackages = {"com.repository"})
public class H2DataSourceConfiguration {
private static final Logger log = LoggerFactory.getLogger(H2DataSourceConfiguration.class);
#Bean(destroyMethod = "shutdown")
#Primary
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.setName("dataSource")
.build();
}
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("com.my.domain", "org.springframework.data.jpa.convert.threeten")
.build();
}
#Bean
#Primary
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(entityManagerFactory);
return jpaTransactionManager;
}
}
The persistence happens in a different transaction to the reading of the data, however they share the same service.
Both operations happen within the #Transactional annotation. Both repository beans are specified in the same service and also contain the #Transactional annotation. The service looks as follows:
#Service
#Transactional
public class MyServiceImpl implements MyService {
private static final Logger log = LoggerFactory.getLogger(MyServiceImpl.class);
#Autowired
private MyJpaRepository myJpaRepository;
#Autowired
private MyJdbcRepository myJdbcRepository;
...
}
MyJdbcRepositoryImpl.java:
#Repository
#Transactional(propagation = Propagataion.MANDATORY)
public class MyJdbcRepositoryImpl implements MyJdbcRepository {
#Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
// methods within here all use jdbcTemplate.query(...)
}
MyJpaRepository.java:
#Repository
#Transactional(propagation = Propagataion.MANDATORY)
public interface AcquisitionJpaRepository extends JpaRepository<AcquisitionEntity, Long> {
}
Is it at all possible that the jdbctemplate calls are saving to a different h2 database?
The above configuration is correct!
The problem was that the JdbcTemplate calls had the schema owner as a prefix.
For example:
select * from I_AM_SCHEMA.KILL_ME
However, I had both the #Entity annotation and the #Table annotation on the entity object and only specified the table name!
Example:
#Entity
#Table(name = "KILL_ME")
So, we were writing to one table with JdbcTemplate but reading from a completely different other table via JPA/Hibernate due to us missing the prefix.
The correct fix was to prefix the entity name in the #Entity annotation:
#Entity("I_AM_SCHEMA.KILL_ME")
DONE!

Spring Boot 1.2.5.RELEASE & Spring Security 4.0.2.RELEASE - How to load configuration file before security context initialization?

I'm facing a problem with Spring: I'm migrating from Spring Security ver. 3.2.7.RELEASE to 4.0.2.RELEASE. Everything was working fine in older version, however a problem occured when it came to loading DataSource.
Let me describe the architecture:
Application is secured with both SAML and LDAP mechanisms (SAML configuration is pretty similar to config given here: https://github.com/vdenotaris/spring-boot-security-saml-sample/blob/master/src/main/java/com/vdenotaris/spring/boot/security/saml/web/config/WebSecurityConfig.java).
They both need to connect to database in order to get some required data. We use MyBatis with Spring Mybatis to get needed data. That's, where the problem begins.
My DAO configuration class looks like this:
#Configuration
#EnableConfigurationProperties
#MapperScan(basePackages = { "pl.myapp" })
public class DaoConfiguration {
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#Primary
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
#Bean
#Primary
public SqlSessionFactoryBean sqlSessionFactoryBean() {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
// some stuff happens here
return sqlSessionFactoryBean;
}
#Bean
#Primary
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
#Bean
#ConfigurationProperties(prefix = "liquibase.datasource")
#ConditionalOnProperty(name="liquibase.enabled")
public DataSource liquibaseDataSource() {
DataSource liquiDataSource = DataSourceBuilder.create().build();
return liquiDataSource;
}
}
In previous version it worked like a charm, but now it has a problem loading mappers, resulting in Bean creation exception on FactoryBean type check: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'someMapper' defined in file [<filename>]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
over and over again (it's not my problem, it's a known Spring/MyBatis bug).
I did some debugging and discovered something interesting: it looks like DaoConfiguration is not treated like a configuration here! I mean: if I add
#Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
return sqlSessionFactoryBean().getObject();
}
to this config, "normal" call of #Bean annotated method should result in calling proper interceptor, here it lacks this funcionality.
My prediction is that: this config class has not been properly wrapped yet and Spring Security already needs beans produced by it.
Is there any solution to properly load this configuration before Spring Security is initialized? Or am I just wrong and missing something (maybe not so) obvious?

Spring Boot does not seem to pick up Atomikos when used for tests

I am working on a prototype for using Spring Boot in our project. We have a JBoss server in production and I was thinking of running integration tests against Undertow embedded server using an embedded transaction manager like Atomikos, because a persistence.xml exists that I have to reuse. My test app context file has the following lines:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#EnableAutoConfiguration
#IntegrationTest("server.port:0")
#ActiveProfiles("test")
public abstract class TestApplicationContext {
...
}
I have also added a custom test configuration as:
#Configuration
public class TestConfiguration {
#Value("${spring.jpa.hibernate.dialect}")
private String dialectClassName;
#Value("${spring.jpa.hibernate.transaction.manager_lookup_class}")
private String transactionManagerClass;
#Bean
public EmbeddedServletContainerFactory servletContainer() {
return new UndertowEmbeddedServletContainerFactory(9000); // Don't know if this can be avoided using some properties
}
#Bean
#ConfigurationProperties(prefix = DataSourceProperties.PREFIX)
public DataSource dataSource() throws Exception {
return DataSourceBuilder.create().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder,
DataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean =
builder.dataSource(dataSource).persistenceUnit("main").build();
Properties additionalProperties = new Properties();
additionalProperties.put("hibernate.dialect", dialectClassName);
additionalProperties.put("hibernate.transaction.manager_lookup_class", transactionManagerClass);
entityManagerFactoryBean.setJpaProperties(additionalProperties);
return entityManagerFactoryBean;
}
#Bean
public PlatformTransactionManager transactionManager() {
// this should not be needed if I have included Atomikos but it seems to pick
// JPA Transaction manager still and fails with the famous NullPointerException at
// CMTTransaction class - because it cannot find a JTA environment
// return new JtaTransactionManager(userTransaction, transactionManager);
}
}
My gradle include for Atomikos is:
testCompile('org.springframework.boot:spring-boot-starter-jta-atomikos')
I am using Spring Boot 1.2.0-RC2.
CAn someone point out what I am doing wrong or how to solve this?
Thanks,
Paddy

Spring #Configuration class needs to be autowired

I've created a Spring #Configuration annotated class and I want to autowire a ResourceLoader to it so that I can use it in one of the #Bean methods to lookup a file given by a String. When I am running the app and initialising the context I get a NPE accessing the autowired field, and in debug mode it is shown as being null/not set. Am I wrong expecting the resourceLoader to be present? Am I wrong asserting the autowiring of the Configuration bean happens before its methods get called? The xml configuration loading this bean is tagged with <context:annotation-config/>
#Configuration
public class ClientConfig {
#Autowired
private ResourceLoader resourceLoader;
public #Bean
String configHome() {
return System.getProperty("CONFIG_HOME");
}
public #Bean
PropertiesFactoryBean appProperties() {
String location = "file:" + configHome() + "/conf/webservice.properties";
PropertiesFactoryBean factoryBean = new PropertiesFactoryBean();
factoryBean.setLocation(resourceLoader.getResource(location));
return factoryBean;
}
}
I'm not sure whether this is a bug or is the expected behavior. Sometimes it worked for me, sometimes didn't. Anyway, there is another way of achieving what you want:
public #Bean PropertiesFactoryBean appProperties(ResourceLoader resourceLoader) {
// resourceLoader is injected correctly
...
}

Resources