Reputation: 1663
I try to create a custom repository
by following this tutorial: https://www.baeldung.com/spring-data-jpa-method-in-all-repositories
My app build fail with error:
NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.dao.ExtendedStudentRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
My full source code: https://github.com/sesong11/springjpa-custom-repo
There are plenty of similar questions, but none of them fixed for me. Perhaps it's Spring issue on current version 2.1.1, or I missed some configuration.
Upvotes: 2
Views: 7930
Reputation: 49656
Before we run the test, let's make sure the main application is running. Actually, it has some issues.
IllegalArgumentException
: Not a managed type: class com.example.demo.entity.Student
.The problem is @EntityScan("com.example.demo.entity.*")
. The package name isn't correct, therefore the Student
class isn't scanned.
The solution is @EntityScan("com.example.demo.entity")
.
PropertyReferenceException
: No property attributeContainsText
found for type Student
!The problem is that the repository base class isn't set, and JpaQueryLookupStrategy
thinks it should construct a RepositoryQuery
from the method name findByAttributeContainsText
. It recognises the pattern findBy{EntityPropertyName}
and fails to find the field attributeContainsText
in Student
.
The repository base class isn't set, because the configuration StudentJPAH2Config
isn't applied. A configuration doesn't get applied if it's unable to be scanned.
@ComponentScan("com.example.demo.dao")
doesn't scan the package where StudentJPAH2Config
resides.
The solution is @ComponentScan("com.example.demo")
which will scan both "com.example.demo"
and "com.example.demo.dao"
packages1.
Now, when the application is fine, let's get back to the test.
NoSuchBeanDefinitionException
: No qualifying bean of type com.example.demo.dao.ExtendedStudentRepository
available.The problem is that StudentJPAH2Config
doesn't constitute the whole configuration, and some beans are missing.
The solution is to list all the configuration classes.
@ContextConfiguration(classes = { StudentJPAH2Config.class, DemoApplication.class })
or
@ContextConfiguration(classes = DemoApplication.class)
or
@SpringBootTest
JpaSystemException
: No default constructor for entity com.example.demo.entity.Student
.The problem is that Hibernate2 requires the default constructor for Student
:
@Entity
public class Student {
@Id
private long id;
private String name;
public Student() {}
public Student(int id, String name) {
this.id = id;
this.name = name;
}
// getters & setters
}
1 @ComponentScan("com.example.demo.dao")
is redundant since this package will be scanned because of @SpringBootApplication
located in there.
2 Hibernate is the default JPA provider in Spring applications.
Upvotes: 2
Reputation: 30449
To get your test working, you have to do the following:
1) Replace the incorrect definition of basePackages
in StudentJPAH2Config
to com.example.demo.dao
, or better remove it as redundant:
@Configuration
@EnableJpaRepositories(repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
}
2) Also replace basePackages
in @ComponentScan
and @EntityScan
in DemoApplication
class to com.example.demo.dao
and com.example.demo.entity
. Or better removed those and @EnableTransactionManagement
annotations at all:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
3) Add a no-argument constructor to the entity Student
.
4) Correct your test class - use @DataJpaTest to test DAO layer and import your StudentJPAH2Config
configuration:
@RunWith(SpringRunner.class)
@DataJpaTest
@Import(StudentJPAH2Config.class)
public class ExtendedStudentRepositoryIntegrationTest {
//...
}
With these corrections, I've run your test successful.
Upvotes: 4
Reputation: 158
You have to remove @ContextConfiguration(classes = { StudentJPAH2Config.class })
and add @SpringBootTest
annotations on your test class ExtendedStudentRepositoryIntegrationTest
it will resolve the error NoSuchBeanDefinitionException
you currently have.
But again I have got error like:
Caused by: java.lang.IllegalArgumentException: Not a managed type: class com.example.demo.entity.Student
which is because Spring is not able to scan Entity Student
.
For this you have to change the @EntityScan("com.example.demo.entity.*")
to @EntityScan("com.example.demo.entity")
on DemoApplication
class.
Again I got the error:
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property attributeContainsText found for type Student!
Which is because the method findByAttributeContainsText()
is not a valid method.
Upvotes: -1
Reputation: 6889
Your implementation of the custom Spring Data JPA repository base class does work. There were just a few items with the Spring Boot Application Context that needed to be addressed. You did have Spring Boot Starter modules declared as dependencies, so I submitted a pull request with changes that moved all the responsibility for manually configuring the context to Spring Auto Configuration factory providers.
Here are some notes about changes that were required to get the tests to pass, along with some brief details on each. Note, to run the Spring Boot Application outside of the test scope, you will want to add an appropriate JDBC vendor dependency to the classpath, along with any spring application properties to define the datasource properties. Spring Auto Config should take care of any EntityManagers and TransactionManagers with reasonable settings after the datasource properties are defined. See Spring Boot features - Working with SQL Databases.
@SpringBootApplication
and @EnableJpaRepositories
). Application pom is configured to depend on Spring Boot Starter modules that provide Spring Auto Configuration classes already through spring.factories while also ensuring all other dependencies are met.@EnableJpaRepositories
does not need to declare basePackages
as Auto Config modules will default to using all packages beneath the Spring Main Application class. Declaration of the additional repositoryBaseClass
is still necessary.@ComponentScan
removed as Auto Config modules will perform a ComponentScan for all packages beneath the Spring Main Application class, which is already at the top level package for the project. Furthermore, this is declaring a basePackages
to scan value that will not include the @Configuration
class StudentJPAH2Config
.@EntityScan
removed as Auto Config modules will perform a scan in all packages beneath the Spring Main Application class.@EnableTransactionManagement
removed as Auto Config modules will perform the same action.application.properties
as they depend on H2
, however the pom only provides that dependency within the test scope's classpath. Furthermore all configuration properties set within the property file are generally Auto Configured by Spring Starters on presence of H2.Student
for hibernate. Hibernate requires this constructor while managing entities, unless @PersistenceConstructor
or @Immutable
is being used which is outside the scope of this issue.ExtendedStudentRepositoryIntegrationTest
. This is the same as the context configuration defined for the Spring Boot integration test in DemoApplicationTests
. When using @ContextConfiguration
directly with StudentJPAH2Config
, the configuration that was defined on the Spring Boot Main Application class (which was the ComponentScan, EntityScan, TransactionManagement) was not being applied to the Spring Test Application Context.Spring Application Starters are (generally) split into two types of dependencies.
autoconfigure
modules that declare a minimal set of dependencies in it's POM, however are built with many dependencies during it's compilation. The auto configure modules also advertise Configuration
classes and factories via a spring.factories
file located in META-INF
of the package. Spring Boot during it's bootstrapping phase will load these configuration classes. Lastly the configuration classes will selectively declare beans and make other application context changes/enhancements based on other beans declared in the context and what dependencies are actually found on the classpath. This allows for general/reasonable default configurations all based on what libraries you are pulling in and a minimal set of properties.starter
modules (generally) do not provide much/any classes. Their purpose is to provide POMs that declare dependencies required for good baselines of development for a particular Spring project. The starter modules also depend on applicable autoconfigure
modules, which pair up with these dependencies to allow you to start running with your application quickly.For the most part, when using Spring Starters, the main driver should not be figuring out what you need to manually configure in the Spring context, but rather, what you should un-configure in case its baseline does not suit your needs. When it comes to following tutorials/examples online like you have, keep in mind that some items surrounding configuration may be shown. There may be times these configuration instructions are necessary, but often they are to show details for configuration required in non-boot Spring applications or for transparency.
Upvotes: 0
Reputation: 673
Following changes were made:
@SpringBootApplication
@ComponentScan("com.example.demo.dao")
@EntityScan("com.example.demo.entity")
@EnableJpaRepositories(basePackages = "com.example.demo.dao",
repositoryBaseClass = ExtendedRepositoryImpl.class)
@EnableTransactionManagement
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
and for StudentJPAH2Config class
@Configuration
@ComponentScan("com.example.demo")
@EnableJpaRepositories(basePackages = "com.example.demo.dao",
repositoryBaseClass = ExtendedRepositoryImpl.class)
public class StudentJPAH2Config {
// additional JPA Configuration
}
Student class which was missing empty constructor:
@Entity
public class Student {
public Student() {
}
public Student(long id, String name) {
this.id = id;
this.name = name;
}
@Id
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And the result
and application running state
Upvotes: 1
Reputation: 158
You have to put @Primary
annotation on the ExtendedRepositoryImpl
class. It should work.
Spring basically not able to qualify the dependency. @Primary
will make sure wherever you are injecting dependency for ExtendedRepository
interface first qualifying bean(class) would be ExtendedRepositoryImpl
.
Upvotes: -1
Reputation: 1065
You need to make below changes to resolve this issue. Also, i executed this with JDK 8 as i don't have JDK 11 in my local. Also, i executed ExtendedStudentRepositoryIntegrationTest.java for the purpose of issue reproduction.
@Configuration @ComponentScan("com.example.demo") @EnableJpaRepositories(basePackages = "com.example.demo.dao", repositoryBaseClass = ExtendedRepositoryImpl.class) public class StudentJPAH2Config {
@SpringBootApplication @ComponentScan("com.example.demo.dao") @EntityScan("com.example.demo.entity") @EnableTransactionManagement public class DemoApplication {
The reason for #1 change is without component scan in test config, spring was not able to look for the dependencies in right packages. #2 is needed as your Student.java is inside com.example.demo.entity package directly, not in any subpackage of it. You can also specify @EntityScan in test config file StudentJPAH2Config, though it's not needed.
Now, this resolve the immediate issue faced by you but still you will be welcomed with some other issues which are pretty straight forward and you would be able to take it forward from here.
Upvotes: -1
Reputation: 1373
You need an annotation in your implemented class. You must add the @Component or @Service annotation in order for Spring to pick up the injection
@Component
public class ExtendedRepositoryImpl<T, ID extends Serializable>
extends SimpleJpaRepository<T, ID> implements ExtendedRepository<T, ID> {
Upvotes: -1