I have a repository in different package than the configuration class , so I annotated it as the following with #Repostiory:
package test;
#Repository
public interface UserTest extends JpaRepository<User, Long> {
}
I have done the component scan on it and it didn't work :
package com.app;
#SpringBootApplication
#ComponentScan({"test","com.app"})
public class Application extends SpringBootServletInitializer {
}
Exception : No qualifying bean of type 'test.UserTest' available: expected at least 1 bean which qualifies as autowire candidate.
why doesn't the component scan work on repository unless I add enableJpaRepositories ? I thought componetScan is enough
Update:
as some of the answers provides solution , I'm asking about explanation not solution . The following will work without even doing component scan on "test" :
SpringBootApplication
#EnableJpaRepositories({"test","com.app"})
public class Application extends SpringBootServletInitializer{
}
Now the question why do I even need to use componentscan on #Repository when it doesn't work ? why in the documentation the #Repository is scanned by componentscan when it doesnt have effect and #EnableJpaRepostiories is enoguh?
from Spring documentation on component scan :
Indicates whether automatic detection of classes annotated with #Component #Repository, #Service, or #Controller should be enabled.
the #Repository in my case is not detected
In order to let spring knows what DataSource is related to what Repository you should define it at the #EnableJpaRepositories annotation.
Try enabling jpa repositories like below.
#SpringBootApplication
#ComponentScan({"test","com.app"})
#EnableJpaRepositories("test")
public class Application extends SpringBootServletInitializer {
}
UPDATE : Why #EnableJpaRepositories needed?
#SpringBootApplication automatically provides the features of the following annotations
#Configuration
#EnableAutoConfiguration
#ComponentScan
But if you try defining your own annotation then spring boot will not take care of internal auto configurations so this is the reason we have to enable repositories.
I have projects in which only #SpringBootApplication is enough if you are not writing your own things.
I hope you got the point.
Golden words :
If you want to get the maximum advantage of spring boot’s auto configuration feature, it is expected to put all your class packages under spring boot main application package (directly in main package or indirectly as sub packages).
I found an explanation about what I was doing wrong. The #Repository annotation with componentscan will not cause spring to implement the spring jpa repository. for the interfaces that implement crud repository enablejparepository should be used.
Now the use of #Repository with componentscan is when you have a repository class and you have your own DAO not for spring curd repo otherwise the implementation won't be created :
#Repository
public class UserTest {
public List<Object> findById(long l) {
.......
}
}
you should use your code like below as #ComponentScan always work with basepackages so your implementation should be like below.
package com.app;
#SpringBootApplication
#ComponentScan(basePackages={"test","com.app"})
#EnableJPARepositories
public class Application extends SpringBootServletInitializer {
}
Related
I'm new to Spring and testing a Spring Data project with Postgresql/JPA and MongoDB components. My Test class has the following annotations:
#SpringBootApplication
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {SpringMongoConfig.class, PgRepository.class, MongodbRepository.class})
public class PerfTest {
#Autowired
private PgRepository pgRepo;
#Autowired
private MongodbRepository mongoRep;
For some reason the spring-context module is trying to load WebMvcConfigurer while resolving bean classes, giving me a NoClassDefFoundError.
Is the Spring Boot Autoconfigure trying to initiate a full web controller suite? If so, why? I'm really just interested in Spring Data. Should I avoid Spring Boot entirely?
You shouldn't annotate a test class as a #SpringBootApplication. This annotation is used to define a class in your main code base which contains a main() method to spin up your Spring boot container with auto-configuration.
If it's Spring data you are interested in testing then your tests will need to point to a #Configuration class which is annotated with #EnableJpaRepositories this will allow your repository interfaces to be autowired.
I don't know what is in your SpringMongoConfig class but if this contains that annotation then all you need to do is:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {SpringMongoConfig.class})
public class PerfTest {
If not then you need to set up this configuration and include it in the #ContextConfiguration of the test.
Another option which is a bit more heavyweight but will guarantee everything is available for your test (provided your Spring boot application is set up correctly) is to mark your test with #SpringBootTest this will automatically load the whole Spring boot context for the application. If it has trouble finding your main class you can point it in the right direction by providing the class in the annotation:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyMainClass.class)
public class PerfTest {
You need to add #Component to your repository interface. Spring loads the component class first and then #AutoWiring & Initialization of variables will happen.
I have packaged my entire entities of the application and the repository interfaces into one jar. The repositories are written with #Repository annotation:
#Repository
public interface InternalUserRepository extends JpaRepository<InternalUser, Long>{
}
I have included this jar file inside my spring boot application and trying to autowire the interface like this from a controller:
#RestController
public class AuthenticationController {
#Autowired
AuthenticationService authenticationService;
#Autowired
InternalUserRepository internalUserRepository;
#GetMapping("/")
public String home() {
return "Hello World!";
}
}
My Main app class is written like:
#SpringBootApplication
#EnableJpaRepositories
#ComponentScan("com.cdac.dao.cdacdao.*")
public class CdacAuthenticationMgntApplication {
public static void main(String[] args) {
SpringApplication.run(CdacAuthenticationMgntApplication.class, args);
}
}
The repository is not getting autowired. When I am firing up the Spring boor application I am getting the following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field internalUserRepository in
com.cdac.user.cdacauthenticationmgnt.controller.AuthenticationController required a bean of type 'com.cdac.dao.cdacdao.repository.InternalUserRepository' that could not be found.
Action:
Consider defining a bean of type 'com.cdac.dao.cdacdao.repository.InternalUserRepository' in your configuration.
Have anyone tried similar architecture like this?
If your JPA repositories are in a different package than your Spring Boot application class, you have to specify that package on the EnableJpaRepositories annotation, not Component:
#EnableJpaRepositories("com.cdac.dao.cdacdao")
The package you specified on ComponentScan is for detecting classes as regular Spring beans, not repository interfaces.
As I remember, #ComponentScan should take a complete path to a package, so I think that your package.* does not work.
Try to use type-safe component scan instead:
// You refer to your packages of your base project and your module here.
// Choose the class so that their package is cover all child package
#SpringBootApplication(scanBasePackageClasses = {xxx. InternalUserRepository.class, xxx.CdacAuthenticationMgntApplication.class})
#EnableJpaRepositories
// No need to explicit #ComponentScan
public class CdacAuthenticationMgntApplication {
Or you can try #EnableJpaRepositories("com.cdac.dao.cdacdao")
Either way, you should pick the class in the most outer package (Spring will aslo try to find bean in sub package of those component scan packages)
Annotation #SpringBootApplication has supported all function
So we don't need config manually
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan(excludeFilters = {
#Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
#Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
We have a base JPA repository class with some additional utility methods that we use in our projects. Following the Spring Data JPA documentation we created the class and use the #EnableJpaRepositories annotation in a configuration class as in the following example:
#Configuration
#EnableJpaRepositories(basePackageClasses = MyApplication.class,
repositoryBaseClass = MyJpaRepositoryImpl.class)
public class SpringDataJpaMyRepositoryConfiguration {
}
We also set the basePackageClasses attribute so our repositories are found, as the configuration class is not in the application root package. Everything works as expected, so no problems so far.
Now we would like to create a spring boot starter to add the repository base class to our projects without further configuration, but we don't know how to do it. If we create an AutoConfiguration class with the EnableJpaRepositories annotation setting the repositoryBaseClass attribute, the automatic repository lookup strategy which looks for repositories under the class annotated with #SpringBootApplication doesn't work anymore.
And we can't use the basePackageClasses attribute as we don't know the main class or package of the project using the autoconfiguration.
Is there any way to do this? Maybe by redefining some bean in our autoconfiguration?
The ideal way would be something that allows to set the repository base class without having to define all the Spring Data JPA autoconfiguration again.
This question has driven me crazy at the time, so I thought I could help you on this.
Basically, the idea is to:
Create a configuration class for your Jpa config
Add #EntityScan and #EnableJpaRepositories referencing the same configuration class as the basePackageClass
Import this configuration class in your autoconfiguration
Create an annotation that you can then reuse where you need your Jpa config
In your example, you're using your Spring application class as your base for scannning.
I've put up a sample project to POC the main ideas at https://github.com/rdlopes/custom-jpa-demo
In the example, there's a project for the JPA entities/repositories exposing a JPA configuration:
#Configuration
#EntityScan(basePackageClasses = JpaConfiguration.class)
#EnableJpaRepositories(basePackageClasses = JpaConfiguration.class,
repositoryBaseClass = BaseRepositoryImpl.class)
public class JpaConfiguration {
}
Be careful with the common implementation for your repositories, you need to show a special signature:
#NoRepositoryBean
public class BaseRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID>
implements BaseRepository<T, ID> {
public BaseRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
}
#Override
public String someCustomMethod(ID id) {
return "Class for entity of id " + id + " is: " + getDomainClass().getSimpleName();
}
}
You can then create your auto configuration as such:
#Configuration
#ConditionalOnClass(CustomJpaRepositories.class)
#Import(JpaConfiguration.class)
public class JpaCustomAutoConfiguration {
}
Providing an annotation to keep things tidy and use it where you need JPA:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
public #interface CustomJpaRepositories {
}
Using your JPA classes will be as simple as having this annotation where you call your JPA repositories:
#SpringBootApplication
#CustomJpaRepositories
public class CustomJpaSampleApplication {
public static void main(String[] args) {
SpringApplication.run(CustomJpaSampleApplication.class, args);
}
#Bean
public CommandLineRunner dataInitializer(UserRepository userRepository) {
return args -> {
User user1 = new User();
user1.setName("user 1");
userRepository.save(user1);
User user2 = new User();
user2.setName("user 2");
userRepository.save(user2);
userRepository.findAll()
.forEach(user -> System.out.println(
userRepository.someCustomMethod(user.getId())));
};
}
}
Hope this helps you getting passed the head scratching moments :-)
EDIT: I've pretty much rewritten my answer - I misunderstood the original question
It's not the nicest solution but the only way I can see this working is by using SpEL inside #EnableJpaRepositories.
This can then go in your auto-configuration and use #ConditionalOnProperty to only auto-configure if the base package property is set
#Configuration
#ConditionalOnProperty("repositories-base-packages")
public class BaseRepositoryAutoConfiguration {
#Configuration
#EnableJpaRepositories(
repositoryBaseClass = MyJpaRepositoryImpl.class,
basePackages = "${repositories-base-packages}"
)
public static class JpaRepositoriesConfig { }
}
Then make sure you have a application.properties or application.yml which defines repositories-base-packages inside your application.
Not sure how you'd declare multiple base packages, my SpEL knowledge is primitive so not sure if it would even be possible.
I have a spring boot application which I am trying to inject a repository. Every time I try to use the repository its null. Here is a snippet of code:
My main application
package com.cisco.ido.no2.deployment;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#EnableAutoConfiguration
#SpringBootApplication
public class No2AlDeviceDeploymentIdApplication {
public static void main(String[] args) {
SpringApplication.run(No2AlDeviceDeploymentIdApplication.class, args);
}
}
My component
#Component
#PostBack(route = "no2.deployment", dataClass = String.class, queue = "no2-deployment")
public class DevicePostbackRunnable extends PostBackRunnable<String> {
#Autowired
private DeviceTypeRepository deviceTypeRepository;
public DevicePostbackRunnable() {}
public DevicePostbackRunnable(Class<String> dataClass) {
super(dataClass);
}
#Override
public void processTaskResult(String result, RunningTask runningTask) {
DeviceType dt = deviceTypeRepository.findOne((long)12); // <-----here
int yy = 90;
}
}
Here is my repository code:
package com.cisco.ido.no2.deployment.repositories;
import com.cisco.ido.no2.deployment.entities.DeviceType;
import org.springframework.data.repository.CrudRepository;
public interface DeviceTypeRepository extends CrudRepository<DeviceType, Long> {
}
My package name for DevicePostBackRunnable is com.cisco.ido.no2.deployment. When I call DeviceType dt = deviceTypeRepository.findOne it is always null. Is there a way to make this work? Also, when I inject this repository in my Rest Controller it works fine.
You should put #Repository in your interface DeviceTypeRepository. So that
the class will automatically be detected by the Spring container as part of the container's component scanning process when you used #Autowired.
From Spring Documentation: http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/beans.html#beans-stereotype-annotations
The #Repository annotation is a marker for any class that fulfills the
role or stereotype of a repository (also known as Data Access Object
or DAO). Among the uses of this marker is the automatic translation of
exceptions as described in Section 20.2.2, “Exception translation”.
Spring provides further stereotype annotations: #Component, #Service,
and #Controller. #Component is a generic stereotype for any
Spring-managed component. #Repository, #Service, and #Controller are
specializations of #Component for more specific use cases, for
example, in the persistence, service, and presentation layers,
respectively. Therefore, you can annotate your component classes with
#Component, but by annotating them with #Repository, #Service, or
#Controller instead, your classes are more properly suited for
processing by tools or associating with aspects. For example, these
stereotype annotations make ideal targets for pointcuts. It is also
possible that #Repository, #Service, and #Controller may carry
additional semantics in future releases of the Spring Framework. Thus,
if you are choosing between using #Component or #Service for your
service layer, #Service is clearly the better choice. Similarly, as
stated above, #Repository is already supported as a marker for
automatic exception translation in your persistence layer.
Your interface should have one of the following annotations in order to be injectable by spring:
#Component
#Service
#Repository
#Controller
Right now i have a inherited project that is using annotation based spring dependency injection. So all classes are simply marked with #Component (or specific stereoTypes like #service, #Repository,#RestController, etc). This makes it a little hard to find where the dependency is located and i was thinking to change it so that each package has its own dependency configuration and then add each package to the #ComponentScan afterwards.
So for example if i had a package called com.mycoolpackage.login and mycoolpackage.networking
then i'd have a Spring configuration like this in first package:
#Configuration
public class LoginDIConfig {
#Bean
public LoginServiceImpl loginServiceImpl() {
return new LoginServiceImpl();
}
}
and in the second package i'd have the following:
#Configuration
public class NetworkDIConfig {
#Bean
public NetworkServiceImpl networkServiceImpl() {
return new NetworkServiceImpl();
}
}
and my#ComponentScan would look like this:
#ComponentScan(basePackages = {"com.mycoolpackage.login","com.mycoolpackage.network"})
So i have two questions about this approach.
How can i use a #Service annotation instead of bean here
Do you think this design is more easier as it tells you what your package dependencies are very easily instead of hunting them
down.
If you want to configure some been properties manually then you should go for above configuration else you should stick with exiting one.
This makes it a little hard to find where the dependency is located
#Autowire Or #Inject annotation will always lead you to dependency class.