Define which entity manager is associated with JpaRepository - spring

I am writing a Spring Boot application which has multiple dataSources and entityManagers and I want to use the JPA CrudRepository interface like:
#Repository
public interface Car extends CrudRepository<Car.class, Long> {}.
I get the following error:
Error creating bean with name 'carRepository': Cannot create inner bean '(inner bean)#350d0774' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#350d0774': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource
I have not been able to figure out how to tell the JpaRepositoryFactory which entity manager to use when building the repositories. It defaults to trying to inject an EntityManagerFactory called: entityManagerFactory

Check this documentation on attributes.
Use the #EnableJpaRepositories annotation below on your Java configuration class
with entityManagerFactoryRef and transactionManagerRef attributes.
#EnableJpaRepositories(basePackages = {
"com.myapp.repositories" }, entityManagerFactoryRef = "entityManagerFactoryRef1", transactionManagerRef = "transactionManagerRef1")
Remove #Repository annotation on Car interface. Spring will register it as JPA repository if you specify basePackages attribute.

what you can do in your spring.xml
<jpa:repositories base-package="com.a.b.repository"
entity-manager-factory-ref="yrEntityMAnagerFectorybeanRef"
transaction-manager-ref="yourTransactionManagerBeanRef"/>
in your EntityManagerFactory bean you will refer your entitymanager bean.

Related

#WebMvcTest with #Import does not work. Test context always asks for #Repository beans

Using Spring Boot 2.7.3 I can not create a simple integration test for my API using #WebMvcTest.
Here is my setup:
// GameServerApplicationTests.kt
#SpringBootTest
class GameServerApplicationTests {
#Test
fun contextLoads() { }
}
// CraftService.kt
#Service
class CraftService {
fun getAll(): List<String> {
return listOf("foo", "bar")
}
}
// CraftApiTest.kt
#WebMvcTest
#Import(value = [CraftService::class])
class CraftApiTest {
#Autowired
private lateinit var testRestTemplate: TestRestTemplate
#Test
fun `should do accept craft all endpoint`() {
val response = testRestTemplate.getForEntity("/craft/all", String::class.java)
assertThat(response.statusCode).isEqualTo(HttpStatus.OK)
}
}
When I run the test I see this exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'itemRepository' defined in com.gameserver.item.ItemRepository defined in #EnableJpaRepositories declared on GameServerApplication: Cannot create inner bean '(inner bean)#3fba233d' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#3fba233d': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
I have no idea why it is looking for the itemRepository bean at all. I never asked for that.
I then added this
#WebMvcTest
#ComponentScan(excludeFilters = [ComponentScan.Filter(Repository::class)]) // <<
#Import(value = [CraftService::class])
Which resulted in this exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'playerRepository' defined in com.gameserver.player.PlayerRepository defined in #EnableJpaRepositories declared on GameServerApplication: Cannot create inner bean '(inner bean)#30c1da48' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#30c1da48': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
Which confuses me even more. I explictly excluded all #Repository beans - but it just skipped ItemRepository and then asked for PlayerRepository now.
I am totally lost and have no idea why I am not able to setup a simple integration test for my API endpoint.
EDIT #1:
Other tests run just fine:
EDIT #2:
I tried to use a #Configuration bean for #Import.
// CraftApiTestConfiguration
#Configuration
class CraftApiTestConfiguration {
#Bean
fun getCraftService(): CraftService {
return CraftService()
}
}
// CraftApiTest.kt
#WebMvcTest
#Import(CraftApiTestConfiguration::class)
class CraftApiTest { // ... }
That did not help either. It just gave me the second exception mentioned above (the one asking for playerRepository)
I'll try to answer although without seeing the actual code it might not be correct.
So #WebMvcTest loads a "slice" of your application with all the beans annotated with #RestControllers. It doesn't load #Service or #Repository annotated beans.
When you run the test with #WebMvcTest annotation it will load all the controllers, and if, by accident the controller references others than the reference on the service (here I can't say for sure what it is), you might end up loading the stuff that you don't actually need.
Now when you use #WebMvcTest there are two things you can/should do:
Work with MockMvc instead of rest template that queries a web server, its not a full-fledged web layer test anyway.
Try using #WebMvcTest with your controller only:
#WebMvcTest(CraftApisController.class)
Also instead of injecting the real implementation of service, you can use #MockBean so that the real service implementation will be covered by a regular unit test (without spring at all, just plain JUnit/Mockito) and this test could check that your annotations are defined correctly

EnversRevisionRepositoryFactoryBean dosenot create bean for JPARepositories

I am using spring boot, hibernate enverse. I have following dependency in pom.xml
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-envers</artifactId>
</dependency>
following is my envers config.
#Configuration
#EnableJpaRepositories(repositoryFactoryBeanClass =
EnversRevisionRepositoryFactoryBean.class, basePackages = {
"com.example.persistence" })
public class EnversConf
{
}
So package com.example.persistence have PersonDAO and AddressDAO and also Entities.
I have following two DAOs,
interface PersonDAO extends RevisionRepository<PersonEntity, Integer, Integer>, JpaRepository<PersonEntity, Integer>{}
interface AddressDAO extends JpaRepository<AddressEntity, Integer>{}
I have two entities PersonEntity which I want to audit and AddressEntity which I don't want to audit.
Now I have following two services,
class PersonServiceImpl implements PersonService{
#Autowire PersonDAO personDAO;
}
class AddressServiceImpl implements AddressService{
#Autowire AddressDAO addressDAO;
}
When I add #EnableJpaRepositories(...) config it unable to get beans for AddressDAO. I thought EnversRevisionRepositoryFactoryBean works for both RevisionRepository and JpaRepository.
I got following exception stacktrace,
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'addressService': Unsatisfied dependency expressed through field 'addressDAO'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'addressDAO': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property findAll found for type AddressEntity!
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'addressDAO': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property findAll found for type AddressEntity!
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property findAll found for type AdressEntity!
Am I missing any configuration.
Got solution ;)
Need to create two separate configuration classes as we cannot use TWO #EnableJpaRepositories on same configuration class.
So have created following two configuration classes,
#EnableJpaRepositories(basePackages = "com.example.jpa.dao")
class JpaConfig {}
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean, basePackages = "com.example.envers.dao")
class EnversConfig {}

Spring request scoped bean failed to be autowired within #Around

I am trying to create a bean that is valid only for a single request that contains a user's roles. I will populate the roles in an #Around method prior to calling any controller methods. I then need to access these roles later for other authorization checks.
#Component
#Aspect
public class SecurityAudit {
#Autowired
private CurrentRoles currentRoles;
#Around("#annotation(requestMapping) && execution( *
com.myapp.controller..*.*(..))")
public Object around(ProceedingJoinPoint pjp, RequestMapping requestMapping)
throws Throwable {
...
...
//I populate the roles with a db lookup. They will be referenced here, and later in controller methods as-needed.
}
}
package com.myapp.model;
...
...
#JsonInclude(JsonInclude.Include.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"sso",
"roles"
})
#Component
#Scope(value="request", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class CurrentRoles {
#JsonProperty("roles")
private Set<Role> roles;
...
...
}
I get the following:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityAudit': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.myapp.model.CurrentRoles com.myapp.currentRoles; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.myapp.model.CurrentRoles] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
The Aspect is created at startup. I would have though the injected request-scoped bean would stay null until requests start coming in, then I could populate the currentRoles bean for that specific request.
Most probably you forgot #Service annotation in your Service class ;)

not able to set the value for autowired parameter

I have datasource autowired with setters. Trying to return datasource value with Bean declaration in Spring javaconfig file. For some reason, it is not identifying and showing the error:
Property 'dataSource' required
Any idea? Here is my Bean in the javaconfig file:
#Bean(name = "dataSource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("xyz");
dataSource.setUsername("xyz");
dataSource.setPassword("xyz");
return dataSource;
}
and the log trace:
Error creating bean with name 'featureStoreSpringJDBC' defined
in URL [jar:file:/C:home/WEB-INF/lib/ff4j-store-springjdbc.jar!
/org/ff4j/store/FeatureStoreSpringJDBC.class]:
Initialization of bean failed; nested exception
is org.springframework.beans.factory.BeanInitializationException
Property 'dataSource' is required for bean 'featureStoreSpringJDBC'
Please note that the attribute dataSource is not annotated with the #Autowired annotation, as a consequence you must explicitely invoke setter and initialize the FeatureStore in javaconfig.
The reason is you should defined the whole FF4J as a bean in the java config.In version before 1.3 it was autowired but we got issues with spreading of javaConfig.

How to create a bean after ServletContext initialized in Spring Boot?

I have a bean, which implements ServletContextAware and BeanFactoryPostProcessor interfaces. I need this this bean register into the applicationContext after the ServletContext finished initialization, because I use some parameters in the servletContext to init this bean.
I am using the Spring Boot, the bean name is SpringBeanProcessorServletAware. I have add it into a configuration bean.
#Bean
public static SpringBeanProcessorServletAware springBeanProcessor() {
SpringBeanProcessorServletAware p = new SpringBeanProcessorServletAware();
return p;
}
My issue is that the bean is created before my container set servletContext to it. Then I can't get the parameters from the servletContext. How to control that the bean must be created after my servletContext has been created completely?

Resources