I am learning Spring Framework and now I am trying to make a simple Spring Boot application that would list all entries from a database (using Hibernate).
First I had a problem when SessionFactory would not be defined, but I managed to define it in Config Class. However, when I try to run the app now, I get the following error:
Description:
The dependencies of some of the beans in the application context form a cycle:
indexController (field private com.prvi.dao.CustomerDAO com.prvi.controllers.IndexController.customerDAO)
↓
customerDAOImpl (field private org.hibernate.SessionFactory com.prvi.dao.CustomerDAOImpl.sessionFactory)
┌─────┐
| sessionFactory defined in class path resource [com/prvi/ConfigPrvi.class]
└─────┘
Basically, I have IndexController, who gets GET / request, then it calls customerDAO to get list of customers and customerDAO uses sessionFactory to get Session and performs a query on DB. (I have omitted Service layer from the app for sake of simplicity)
Now, I have read that this error happens when a bean is dependent on a bean that is dependent on a first bean, making cyclical dependency. However, I do not understand where I made this cycle and how to fix it. Also, other answers on this topic have not provided me enough information to correct the error. They were mostly oversimplifies where cycle is clear, which is not the case here.
Here is what I have tried so far:
PrviApplication.java - entry point for Spring Boot
#SpringBootApplication
public class PrviApplication {
public static void main(String[] args) {
SpringApplication.run(PrviApplication.class, args);
}
}
ConfigPrvi.java - My Configuration file
#Configuration
#EnableAutoConfiguration
public class ConfigPrvi {
#Bean
public HibernateJpaSessionFactoryBean sessionFactory(EntityManagerFactory emf){
HibernateJpaSessionFactoryBean factory = new HibernateJpaSessionFactoryBean();
factory.setEntityManagerFactory(emf);
return factory;
}
}
IndexController.java - my Controller class, which handles GET /
#Controller
#RequestMapping("/")
public class IndexController {
#Autowired
private CustomerDAO customerDAO;
#GetMapping("/")
public String listCustomers(Model model){
model.addAttribute("customers", customerDAO.getAllCustomers());
return "index";
}
}
CustomerDAO.java - just an interface
public interface CustomerDAO {
public List<Customer> getAllCustomers();
}
CustomerDAOImpl.java - implementation, retrieves data from database
#Repository
public class CustomerDAOImpl implements CustomerDAO {
#Autowired
private SessionFactory sessionFactory;
#Transactional
public List<Customer> getAllCustomers(){
Session session = sessionFactory.getCurrentSession();
Query<Customer> query = session.createQuery("from Customer order by lastName", Customer.class);
List<Customer> customers = query.getResultList();
return customers;
}
}
Is it perhaps that cycle was made here:
main --> controller --> dao --> sessionFactory --> config --> main
If so, how can I rewrite the code so that I get rid of it?
EDIT: Added pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.prvi</groupId>
<artifactId>prvi</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>
<name>prvi</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.10.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.2.10.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Related
In sandbox project trying reactive webflux with springboot and postgres. I added r2dbc, created endpoints - get all records and post endpoints work, but appeared problem with requests which use path variable (get one record, or delete by id)
Here is my code:
#SpringBootApplication
#EnableR2dbcAuditing
public class Springboot2Application {
public static void main(String[] args) {
SpringApplication.run(Springboot2Application.class, args);
}
}
Controller:
#RequiredArgsConstructor
#RestController
#RequestMapping("/api/v1")
public class ToDoController {
private final ToDoRepository repository;
#GetMapping(value = "/to-do/{toDoId}", produces = {
MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE})
public Mono<ResponseEntity<ToDo>> getToDo(#Valid #PathVariable Long toDoId) {
return repository.findById(toDoId)
.map(ResponseEntity::ok);
}
}
repository:
#Repository
public interface ToDoRepository extends R2dbcRepository<ToDo,Long> {
}
entity:
#Data
#RequiredArgsConstructor
#Table(name = "to_do")
public class ToDo {
#Id
private Long id;
#Version
private Long version;
#NotNull
#NotBlank
private String description;
#CreatedDate
private Timestamp created;
#LastModifiedDate
private Timestamp modified;
private boolean completed;
}
r2dbc config:
#Configuration
#EnableR2dbcRepositories(basePackages = "com.springboot2.repository")
public class R2DBCConfig extends AbstractR2dbcConfiguration {
#Bean
public ConnectionFactory connectionFactory() {
return ConnectionFactories.get(
ConnectionFactoryOptions.builder()
.option(DRIVER, "postgresql")
.option(HOST, "localhost")
.option(PORT, 5432)
.option(USER, "admin")
.option(PASSWORD, "admin")
.option(DATABASE, "springdb")
.build());
}
#Bean
ReactiveTransactionManager transactionManager(ConnectionFactory connectionFactory) {
return new R2dbcTransactionManager(connectionFactory);
}
}
And my pom file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.springboot</groupId>
<artifactId>springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot2</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
</properties>
<dependencies>
<!-- DB,ORM, and plugins-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<version>0.8.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Reactive libs-->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
table creation sql:
CREATE TABLE to_do
(
ID SERIAL primary key ,
version bigint,
description char(255),
created timestamp,
modified timestamp,
completed boolean
);
So when I execute GET http://localhost:8080/api/v1/to-do/3 I get:
java.lang.IllegalStateException: Required identifier property not found for class com.springboot2.domain.ToDo
I tried to define this using #Query in repository, but got same result. I suppose problem can be either in my table, or in entity, but I can't see it. And also seems #Version does not work(every time writes null to table)
The problem may be in wrong import of #Id annotation in your domain.
It should be from package
org.springframework.data.annotation.Id
But in your case it is from package
javax.persistence
Also, consider the naming of fields. As in your domain id is declared as "id", I can see in your create script id as "ID" (in uppercase).
I'm trying to write a simple CRUD program and I get this error. The program is based after codecademy project. Not sure why I doesn't work.
If I comment out the constructor the error disappears.I don't have anything in my properties.
Can someone give me a hand?
Description:
Parameter 0 of constructor in com.example.FitApp3.controller.FoodController required a bean of type 'com.example.FitApp3.repository.FoodRepository' that could not be found.
Action:
Consider defining a bean of type 'com.example.FitApp3.repository.FoodRepository' in your configuration.
Process finished with exit code 1
This is my code:
Entity/Food.java
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class Food extends com.example.FitApp3.model.Entity {
private String foodName;
private int foodKcal;
private int foodProtein;
private int foodCarb;
private int foodFat;
public String getFoodName() {
return foodName;
}
public void setFoodName(String foodName) {
this.foodName = foodName;
}
public int getFoodKcal() {
return foodKcal;
}
public void setFoodKcal(int foodKcal) {
this.foodKcal = foodKcal;
}
public int getFoodProtein() {
return foodProtein;
}
public void setFoodProtein(int foodProtein) {
this.foodProtein = foodProtein;
}
public int getFoodCarb() {
return foodCarb;
}
public void setFoodCarb(int foodCarb) {
this.foodCarb = foodCarb;
}
public int getFoodFat() {
return foodFat;
}
public void setFoodFat(int foodFat) {
this.foodFat = foodFat;
}
}
Repository/FoodRepository.java
public interface FoodRepository extends CrudRepository<Food, Integer> {}
Controller/FoodController.java
#RestController
public class FoodController {
private FoodRepository foodRepository;
public FoodController(FoodRepository foodRepository) {
this.foodRepository = foodRepository;
}
}
Mainclass
#SpringBootApplication
public class FitApp3Application {
public static void main(String[] args) {
SpringApplication.run(FitApp3Application.class, args);
System.out.println("hello world");
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>FitApp3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>FitApp3</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.5.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Can you add #Repository annotation to FoodRepository interface.
#Repository
public interface FoodRepository extends CrudRepository<Food, Integer> {}
You need to replace spring-boot-starter-jdbc with spring-boot-starter-data-jpa as follows:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
You can have a look at the following links the difference between them, but basically, JPA helps you deal with your Database data by mapping it directly to Java objects:
https://www.baeldung.com/jpa-vs-jdbc
JPA or JDBC, how are they different?
I think you are just a little bit confused with spring-jdbc, spring-data and spring persistence interfaces abstraction, let me help. TL;DR solution is at the bottom:
In the constructor of FoodController you are declaring FoodRepository as a parameter, so spring have to find the bean of this time at runtime in order to create bean of FoodController. You have declared FoodRepository and extend it from CrudRepository. I guess, that you have done it with the assumption, that spring will create an implementation of FoodRepository at runtime (because it extends CrudRepository). But, unfortunately, it is not the spring-core module, that will create bean of FoodRepsitory, nor it is spring-jdbc. This interfaces are the part of spring-data project. So, for creation of the bean of the type FoodRepository is responsible current spring-data project in your classpath (I mean, it could be spring-data-jdbc, spring-data-jpa or whatever). This interfaces (CrudRepository, Repository e.t.c.) are common for all of the spring data projects, so they are shipped in spring-data-commons jar, this is why you have them available in classpath, because you have included spring-data-commons explicitly:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.5.1</version>
</dependency>
Another thing is that this starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
does not do anything with spring-data project - it just brings spring-jdbc, Hikari connection pool, and some other spring-boot stuff. In other words, the problem is that you do not have any spring-data project in your classpath, thats why FoodRepository bean is not created.
Also note: #Entity does not make sense here, becuase you do not have any jpa persistence provider in classpath
Solution:
To solve the problem, I suggest you to
Remove spring-data-commons dependency from pom.xml
Include spring-boot-starter-data-jpa into your project (into pom.xml). It will bring the appropriate version of spring-data-commons dependency.
Hope it helped, have a nice day!
Add #Repository annotation to FoodRepository interface
and also #Autowired annotation to the FoodRepository in FoodController.
*Ehcache3 not working with spring boot - I tried out with approach given below. Spring boot never caches the value mentioned in the component.It is getting called n - no of times no matter the cache is enable or not. In the logs it shows cache is added to cache manager but thats not the case here
ehcache.xml
<ehcache:config>
<ehcache:cache-template name="myDefaultTemplate">
<ehcache:expiry>
<ehcache:none/>
</ehcache:expiry>
</ehcache:cache-template>
<ehcache:cache alias="customer" uses-template="myDefaultTemplate">
<ehcache:key-type>java.lang.Long</ehcache:key-type>
<ehcache:value-type>com.controller.Customer</ehcache:value-type>
<ehcache:expiry>
<ehcache:tti unit="seconds">30</ehcache:tti>
</ehcache:expiry>
<ehcache:heap unit="entries">200</ehcache:heap>
</ehcache:cache>
</ehcache:config>
In my pom.xml i have the following configurations -
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.test</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.6.2</version>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Application.java which starts spring boot app
#SpringBootApplication
#ComponentScan(basePackages = {"com.service"})
#EnableCaching
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
Component class for caching -
#Component
public class CustomerService {
#Cacheable(cacheNames = "customer",key="#id")
public Customer getCustomer(final Long id){
System.out.println("Returning customer information for customer id
{}
"+id);
Customer customer = new Customer();
customer.setCustomerId(id);
customer.setFirstName("Test");
customer.setEmail("contact-us#test.com");
return customer;
}
}
I tried with couple of approaches by adding component scan in the application
but didn't worked out.
Spring boot starts and it shows cache has been added to cache manager.
I got it working by changing from #Component to #Service. I don't understand why caching is not working under component and works in service layer
If you want to add Caching annotation at the Repository layer then just remove #Repository Annotation, If you want to add Caching at Service Layer then use #Service Annotation instead of #Component Annotation.
I have some project trying to build a microservices architecture. Some of the projects are used producing jar files (I refer these projects as "library projects".) for the callable microservices. One of these microservices are responsible for user/customer registration and lookup. All the data is persisted in a Cassandra cluster. One of the library projects are responsible for providing various Cassandra based services/value objects, etc...
In the microservice pom file, I include the jar produced by the library
project. It compiles, but when I start the customer service the classes from that jar are not found.
***************************
APPLICATION FAILED TO START
***************************
Description:
Field repository in com.besztercekk.tao.customer.handler.PostUserHandler required a bean of type 'com.besztercekk.tao.cassandra.repository.ReactiveUserRepository' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.besztercekk.tao.cassandra.repository.ReactiveUserRepository' in your configuration.
Here are some code snippets, but I can attach anything else you need.
This is the pom file for the microservice. The class what is not found is in this dependency: tao-elszamolas-cassandra.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>tao-elszamolas-customer-service</artifactId>
<packaging>jar</packaging>
<name>tao-elszamolas-customer-service</name>
<description>TAO elszamolas customer backend service</description>
<parent>
<groupId>com.besztercekk.tao</groupId>
<artifactId>tao-elszamolas-spring</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<dependencies>
<!-- Spring Cloud Eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- Spring Cloud config client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<!-- Webflux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Reactive cassandra -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.1-jre</version>
</dependency>
<!-- TAO elszamolas dependencies -->
<dependency>
<groupId>com.besztercekk.tao</groupId>
<artifactId>tao-elszamolas-cassandra</artifactId>
</dependency>
<dependency>
<groupId>com.besztercekk.tao</groupId>
<artifactId>tao-elszamolas-jwt</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
This is the pom file for the project what contains the missing class:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>tao-elszamolas-cassandra</artifactId>
<packaging>jar</packaging>
<name>tao-elszamolas-cassandra</name>
<description>TAO elszámolás Spring alapú több projektben használt komponensek gyűjtő projektje</description>
<parent>
<groupId>com.besztercekk.tao</groupId>
<artifactId>tao-elszamolas-spring</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencies>
<!-- Reactive cassandra -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-cassandra-reactive</artifactId>
</dependency>
</dependencies>
</project>
Here the class where the error is coming from:
#Component
#Validator(UserDataValidator.class)
public class PostUserHandler extends AbstractValidationHandler<User, ChainableValidator> {
#Autowired
private ReactiveUserRepository repository;
#Autowired
private PasswordEncoder passwordEncoder;
public PostUserHandler() {
super(User.class);
}
#Override
protected Mono<ServerResponse> processRequest(User body, ServerRequest request) {
return repository.save(enrichUser(body, UUIDs.random().toString()))
.flatMap(updatedUser -> {
sendRegistrationCode();
return ok(updatedUser);
})
.onErrorResume(IllegalArgumentException.class, e -> ServerResponse.badRequest().build())
.switchIfEmpty(ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR).build());
}
#Override
protected Mono<User> extractContent(ServerRequest request) {
return request.bodyToMono(User.class);
}
private void sendRegistrationCode() {
//TODO: Send Kafka message to email component
}
private Mono<ServerResponse> ok(User c) {
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromPublisher(Mono.just(c), User.class));
}
private User enrichUser(User user, String activationCode) {
return User.builder()
.authorities(user.getAuthorities())
.email(user.getEmail())
.enabled(user.isEnabled())
.firstName(user.getFirstName())
.id(UUIDs.timeBased())
.lastName(user.getLastName())
.middleName(user.getMiddleName())
.password(passwordEncoder.encode(user.getPassword()))
.registrationTime(LocalDateTime.now())
.passwordExpiryTime(LocalDateTime.now())
.roles(user.getRoles())
.username(user.getUsername())
.accountExpired(false)
.accountLocked(false)
.activationCode(activationCode)
.build();
}
}
Any help would be appreciated!
UPDATE (SOME EXTRA INFO ADDED):
Here is the main class containing the component scan.
#SpringBootApplication
#EnableEurekaClient
#ComponentScan(basePackages = {"com.besztercekk.tao.customer", "com.besztercekk.tao.cassandra"})
public class CustomerBackendApplication {
public static void main(String[] args) {
SpringApplication.run(CustomerBackendApplication.class, args);
}
}
Here is the repository class:
package com.besztercekk.tao.cassandra.repository;
import java.util.UUID;
import org.springframework.data.cassandra.repository.ReactiveCassandraRepository;
import org.springframework.stereotype.Component;
import com.besztercekk.tao.cassandra.model.User;
import reactor.core.publisher.Mono;
/**
* This interface provides reactive {#link User} services.
*/
public interface ReactiveUserRepository extends ReactiveCassandraRepository<User, UUID> {
Mono<User> findByUsername(String username);
Mono<User> findByEmail(String email);
}
Also tried to add #Component and #Repository annotations to this interface, just to see its effect. :-)
My other idea was add the bean programatically to the context after restart. But this is just an interface, so not sure if it works out at all.
The third idea was wrap this interface into a service class and that is easy to add programatically.
But I really want it to be added automatically, without writing code.
Try adding the following annotation to any of the #Configuration classes:
#EnableReactiveCassandraRepositories
#Configuration
class MyConfiguration() {
//code
}
The above code enables spring to automatically scan and create the default implementation bean for your interface ReactiveUserRepository .
Also annotate the interface like :
#Repository
public interface ReactiveUserRepository extends ReactiveCassandraRepository<User, UUID> {
Mono<User> findByUsername(String username);
Mono<User> findByEmail(String email);
}
For more details read :
https://dzone.com/articles/reactive-streams-with-spring-data-cassandra
Also, if you would like more details on the annotations read a similar post :
how annotation #Repository in java spring work?
I'm trying to structure a project to connect to MongoDB using Spring Data as below:
SpringMongoConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import com.mongodb.MongoClient;
#Configuration
public class SpringMongoConfig {
#Bean
public MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(new MongoClient("127.0.0.1"), "ReconInput");
}
#Bean
public MongoTemplate mongoTemplate() throws Exception {
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory());
return mongoTemplate;
}
}
ReconInputRepository.java:
#Repository
public interface ReconInputRepository extends MongoRepository<ReconInput, String> {
public List<ReconInput> findByReportingDate(String reportingDate);
}
ReconInputService.java
public interface ReconInputService {
public List<ReconInput> getInputByReportingDate(String reportingDate);
}
ReconInputServiceImpl.java
#Service
public class ReconInputServiceImpl implements ReconInputService {
#Autowired
private ReconInputRepository reconInputRepository;
public List<ReconInput> getInputByReportingDate(String reportingDate) {
return reconInputRepository.findByReportingDate(reportingDate);
}
}
App.java
public class App
{
public static void main( String[] args )
{
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
ReconInputService reconInputService = ctx.getBean(ReconInputService.class);
List<ReconInput> inputData = reconInputService.getInputByReportingDate("2017 Nov 20");
System.out.println(inputData.get(0).getReportId());
}
}
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ups.mongodb</groupId>
<artifactId>MongoConnection</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MongoConnection</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>5.0.1.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.5.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-mongodb -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
</dependencies>
</project>
When I run the project, it throw an exception:
No qualifying bean of type 'ups.mongo.service.ReconInputService'
available.
Please help me any suggestion for this error. Thank you !
Update 1
Added #ComponentScan(basePackages = "ups.mongo") to SpringMongoConfig.java.
Then I got new issue:
Error creating bean with name 'ReconInputService': Unsatisfied dependency expressed through field 'reconInputRepository';
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'ups.mongo.repository.ReconInputRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Update 2
Instead of using Spring Data MongoRepository. I replaced ReconInputRepository.java that extends from Spring data by implement it by myself as below :
ReconInputRepository.java
public interface ReconInputRepository {
public List<ReconInput> findByReportingDate(String reportingDate);
}
ReconInputRepositoryImpl.java
#Repository
public class ReconInputRepositoryImpl implements ReconInputRepository {
#Autowired
MongoTemplate mongoTemplate;
public List<ReconInput> findByReportingDate(String reportingDate) {
List<ReconInput> reconInputList = null;
Query searchUserQuery = new Query(Criteria.where("reportingDate").is(reportingDate));
reconInputList = mongoTemplate.find(searchUserQuery, ReconInput.class);
return reconInputList;
}
}
Then it work correctly.
My Summary
The issue may come from Spring does not support inject interface - as #amdg suggest (but work in spring boot - I have no idea why, if someone know that please leave me some comment).
Reference: Spring interface injection example
Update 3
At last, I found the most simple way to make it correctly.
All I need to do is adding #EnableMongoRepositories({ "ups.mongo.repository" }) to the SpringMongoConfig.java
As I suspected, you mix plain old Spring with Spring Boot and want to get Spring Boot effect.
In order to use Spring Boot you should update your dependencies to use Spring Boot Starters.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ups.mongodb</groupId>
<artifactId>MongoConnection</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MongoConnection</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Then just add your config in the root package of your app (presumably it's ups.mongo):
#SpringBootApplication
#EnableMongoRepositories
public class App
{
public static void main( String[] args )
{
ConfigurableApplicationContext ctx = SpringApplication.run(App.class, args);
ReconInputService reconInputService = ctx.getBean(ReconInputService.class);
List<ReconInput> inputData = reconInputService.getInputByReportingDate("2017 Nov 20");
System.out.println(inputData.get(0).getReportId());
}
}
In this case, you do not even need SpringMongoConfig.class. Instead, add the following config in your application.properties:
spring.data.mongodb.host=127.0.0.1
spring.data.mongodb.port=27017
spring.data.mongodb.database=demo
Since you have annotated #Service on ReconInputServiceImpl so,
please add ReconInputServiceImpl.class in main class
ReconInputService reconInputService = ctx.getBean(ReconInputServiceImpl.class);
You can annotate your ReconInputServiceImpl class as #Service("reconInputService").
You can then access it as follows.
ReconInputService reconInputService = ctx.getBean("reconInputService",ReconInputService.class);
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
If you use above constructor, DI container will only load bean definitions from SpringMongoConfig class.
If You want DI container to scan all bean definitions recursively for a particular base package please use this constructor instead
AnnotationConfigApplicationContext(java.lang.String... basePackages)
I have previously added only spring-data-mongodb package which kept me busying debugging for "No qualifying bean for some repository".
After adding spring-boot-starter-data-mongodb, everything is handled by spring.
application.yaml
spring:
data:
mongodb:
uri: mongodb://username:password#localhost:27017
database: test
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>