I have a class ServiceA on project-a:
public class ServiceA {
private ModelA modelA;
public ServiceA(ModelA modelA) {
this.modelA = modelA;
}
}
modelA from other local library (external library). modelA have #Component annotation
when I run this code, cannot found error bean on ModelA. I solve with add #Bean for ModelA on project-a.
Why I should add bean? because the ModelA on external library? Any reference link for I can understand for this case? I want understand for this code. Thank you
The first comment is the most likely answer to your issue;
specifically,
the component scanning that is configured in your project does not include the package that contains the modelA class.
I've got a fairly standard spring boot app which is built with gradle from several gradle modules. Here's the directory layout:
- root
- serviceA
- src/main/java
- org.example.serviceA
- ServiceAApplication.java
- serviceB
- serviceC
- common
- src/main/java
- org.example.common
- CommonSecurityConfiguration.java
What I would like to do is to include the CommonSecurityConfiguration class from the shared common module in serviceA. Note that ServiceAApplication and CommonSecurityConfiguration reside in different base packages.
I tried to use #Import(CommonSecurityConfiguration.class) on my ServiceAApplication, but that had no observable effect at all.
The only thing which worked was to annotate ServiceAApplication like so:
#SpringBootApplication(basePackages = { "org.example.serviceA", "org.example.common"})
public class ServiceAApplication { ... }
This approach works, but seems very coarse grained to me - it will import each and every component and configuration it finds in org.example.common.
Is there a better way to do this? Can I include individual classes into the component scan by listing them one by one?
Try to use
#Import(CommonSecurityConfiguration.class) above configuration class. So it would look like this:
#Configuration
#Import(CommonSecurityConfiguration.class)
public class ServiceAConfiguration { ... }
I believe what you are looking for is #CompnentScan("com.example"), this will tell Spring to look at all the files under the specified path recursively. (In this case it would be #ComponentScan("root"))
You find more info here: baeldun.com/spring-component-scanning
Hope this helps.
Since you want to control which components are brought in , we can make an annotation , let's call that annotation PickyComponentImport
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface PickyComponentImport{
}
Then on our SpringBootApplication annotation we can add a new filter which looks for this annotation.
#ComponentScan(basePackages = { "org.example.serviceA",
"org.example.common" }, includeFilters = #Filter(PickyComponentImport.class))
public class ServiceAApplication { ... }
Then we can just add that annotation on any class we want included
#Configuration
#PickyComponentImport
public class CommonSecurityConfiguration {
}
EDIT: I think if you go with this approach you can just componentScan basepackage as root.
I have a projected checked into GitHub here
https://github.com/romeoopk/demo
Please note that this is not a "complete" working project but in progress!
I have two data sources (h2 mem DB and Cassandra)
The aim of the project is to hide the implementation behind the Service.
there are two profiles I am looking against
dev - goes against h2
test - goes against Cassandra
when I run against test, it runs fine as expected but when I run against dev, I get the following message
Parameter 0 of constructor in com.example.demo.service.H2HotelServiceImpl required a bean of type 'com.example.demo.repository.HotelRepository' that could not be found.
Action:
Consider defining a bean of type 'com.example.demo.repository.HotelRepository' in your configuration.
I am unsure, how to have a proper injection so that the H2HotelRepository and H2HotelByLetterRepository get used for querying towards H2
any help is highly appreciated!!!
Your repository classes under cassandra folder is like this
#Repository
#Profile("test")
public class CassandraHotelRepository implements HotelRepository<Hotel> {
....
}
#Repository
#Profile("test")
public class CassandraHotelByLetterRepository implements HotelByLetterRepository<HotelByLetter, HotelByLetterKey> {
....
}
But in your h2 folder you have declared as
#Repository
#Profile("dev")
public abstract class H2HotelRepository implements CrudRepository<Hotel, String>, HotelRepository<Hotel> {
...
}
#Repository
#Profile("dev")
public abstract class H2HotelByLetterRepository implements CrudRepository<HotelByLetter, HotelByLetterKey>, HotelByLetterRepository<HotelByLetter, HotelByLetterKey> {
.....
}
As you can clearly see in h2 folder i.e. for profile dev, there is no concrete class. Both of your repositories under h2 are abstract.
Remove abstract and it should work fine.
I am using Room architecture component for persistence. I have created generic DAO interface to avoid boilerplate code.
Room Pro Tips
But my code doesn't compile saying "Error:(21, 19) error: Type of the parameter must be a class annotated with #Entity or a collection/array of it." for the Generic class T.
interface BaseDao<T> {
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(T... entity);
#Update
void update(T entity);
#Delete
void delete(T entity);
}
#Dao
public abstract class ReasonDao implements BaseDao<ReasonDao> {
#Query("SELECT * from Reason")
abstract public List<Reason> getReasons();
}
Is there anything I am missing here.
It works like this here
Changed in gradle from this:
kapt "androidx.room:room-compiler:$roomVersion"
to this:
annotationProcessor "androidx.room:room-compiler:$room_version"
I had initially followed the method used in Kotlin, but that gives the error in Java code.
Two quick changes fixed it for me
Change BaseDao to Abstract class
Added #Dao annotation to the BaseDao
Please find the code below and now it runs properly
#Dao
abstract class BaseDao<T> {
#Insert(onConflict = OnConflictStrategy.REPLACE)
abstract void insert(T entity);
#Update
abstract void update(T entity);
#Delete
abstract void delete(T entity);
}
#Dao
public abstract class ReasonDao extends BaseDao<Reason>{
#Query("SELECT * from Reason")
abstract public List<Reason> getReasons();
}
The problem was that in my build.gradle, the version of Kotlin I was using was 1.5.0
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.5.0"
But as far as I understood, this version of Kotlin along with Room and coroutines doesn’t work well.
Was able to resolve the issue by downgrading the Kotlin version to:
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.32"
Solved! Thank me later
If you faced this issue in Kotlin 1.7.10+ and Room < 2.4.2, upgrading Room to 2.4.3 fixes the issue.
From Room's changelog for 2.4.3:
Fixed an issue that would cause Room to not recognize suspend functions in Kotlin 1.7
This issue was due to using suspend functions for insert/delete in interfaces:
https://issuetracker.google.com/issues/236612358
The reason is that you specified ReasonDao type as generic parameter instead of Reason.
Original code:
#Dao
public abstract class ReasonDao implements BaseDao<ReasonDao> {
...
}
Correct code:
#Dao
public abstract class ReasonDao implements BaseDao<Reason> {
...
}
where Reason is the type marked with #Entity annotation.
By the way, this is fixed in the accepted answer, but is not mentioned in the changelist :)
Changed in gradle this: kapt "androidx.room:room-compiler:$roomVersion" to this: annotationProcessor "androidx.room:room-compiler:$room_version"
Doing this solution served me partially but when trying to insert a date in the database it did not work for me so try kapt "androidx.room:room-compiler:$roomVersion" and rather change room_version to the latest stable version https://developer.android.com/jetpack/androidx/releases/room#groovy
ependencies {
//**
def room_version = '2.4.0'
def activityVersion = '1.4.0'
def lifecycle_version = "2.2.0"
// Room and Lifecycle dependencies
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
//kotlin extensions for coroutine support with room
implementation("androidx.room:room-ktx:$room_version")
//kotlin extension for coroutine support with activities
implementation "androidx.activity:activity-ktx:$activityVersion"
//**
This is how it worked for me correctly
remove suspend,then it will work.
#Dao
interface FoodDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun upsert(meal: Meal): Long
#Query("SELECT * FROM food_tables")
fun getAllFood(): LiveData<List<Meal>>
#Delete
fun deleteFood(meal: Meal)
}
In my case I tried to save to DB non-Entity objects. Then replaced with Entity class (contains #Entity(tableName = "your_table", indices = [Index("your_key")])).
I believe is that you have missed to give Entity annotation to T class. For example Reason class should have #Entity and give it to ReasonDao class. Like:
#Dao
public abstract class ReasonDao extends BaseDao<Reason>{}
This is a version error. Try updating your room dependencies.
I changed mine from:implementation "androidx.room:room-runtime:2.2.5"
To :implementation "androidx.room:room-runtime:2.4.2"
What is missing here is the Data class. Generally,
#Entity represents objects you want to store,
Room entity includes fields for each column inside the
table in the database
#Entity(tableName="something") data class YourData()
For those who has a "Type of the parameter must be a class annotated with #Entity or a collection/array of it " error when using Kotlin in the dao , you should try using #JvmSuppressWildcards annotation on your functions. eg.
#Query("SELECT * FROM materials")
#JvmSuppressWildcards
fun getAllMaterials(): LiveData<List<MaterialModel>>
In my case I was trying to store a data class object into the Room Database. The error says that the parameter for Insert should be a class which has an annotation of #Entity so I created a table (entity) which has same values as data class and stored them successfully
One small mistake I did was
I did not annotate my data class with the #Entity annotation
Table declaration
#Entity // Check if you have used this annotation or not
data class fooTable(){
...
}
In DAO
#Dao
interface myDao{
#Insert
suspend fun addFoo(fooObj: fooTable)
}
}
If you are trying to insert a row in a table in Room DB then the corresponding data class should be annotated with #Entity annotation
Actually changing the version room matters too, like I changed my room version from 2.2.6 to 2.4.3 and it worked. if the Kotlin version is low it's better to use a lower version of the room as well if the Kotlin version is high it's better to use a higher version too.
Is there a way to create Java-based configuration class that does exactly same as #ComponentScan(basePackageClasses = {X.class}) ?
I have to create generic configuration class (for extending in tests) doing something like that:
#Configuration
#ComponentScan(basePackageClasses = T.class)
public class EndpointTestConfig<T> {
}
It's impossible to use generics with annotations so I would need the same effect using Java.
I'm not sure what exactly you are trying to achieve. If I understand you correctly you have a base class T and a lot of derived classes? And you want to have a base config that will create common beans and every extender will create a bean of class that extends T? So you can do something like that:
#Configuration
public class BaseTestConfig {
//common beans
}
#Configuration
#Import(BaseTestConfig.class)
public class ConcreteEndpointConfig {
#Bean
public YourDerivedClass bean() {
return new YourDerivedClass();
}
}
I hope that I got your idea. If this is not what you need please elaborate.