Implement Interface with custom method and JpaRepository - java

so I'm working on an API Rest, registration for students, using Domain Driven Design. So my problem is: I'm trying to add a custom method with the JpaRepository. My Project hierarchy is:
com.wizardry.witchcraft.domain.model.StudentModel;
com.wizardry.witchcraft.infraestructure.repository.CustomRepositoryImpl;
com.wizardry.witchcraft.domain.repository.IStudentRepository;
com.wizardry.witchcraft.domain.repository.ICustomRepository;
com.wizardry.witchcraft.api.controller.TesteController2;
com.wizardry.witchcraft.api.controller.StudentController;
So first I've created the method Implementation
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import com.wizardry.witchcraft.domain.model.StudentModel;
import com.wizardry.witchcraft.domain.repository.ICustomRepository;
#Repository
public class CustomRepositoryImpl implements ICustomRepository {
#PersistenceContext
private EntityManager manager;
#Override
public List<StudentModel> findCustom (String name){
//METHOD
}
}
And the Interface:
import com.wizardry.witchcraft.domain.model.StudentModel;
public interface ICustomRepository {
List<StudentModel> findCustom(String name);
}
And to test it out I created a new Controller to don't mess with my working one, TesteController2, and it worked fine. So my next step was extended the ICustomRepository in the IStudentRepository, made the changes in TesteController2 and then Spring won't find my findCustom method anymore, It tries to create the method as a JPA keyword and return and error. This is my repository interface:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.wizardry.witchcraft.domain.model.StudentModel;
#Repository
public interface IStudentRepository extends ICustomRepository, JpaRepository<StudentModel, Long> {
List<StudentModel> queryByName(String name, #Param ("id") Long school);
List<StudentModel> queryFirstByNameContaining(String name);
List<StudentModel> queryTop2ByNameContaining(String name);
int countByWizardingSchoolModelId(Long school);
}
And the TesteController2:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import com.wizardry.witchcraft.domain.model.StudentModel;
import com.wizardry.witchcraft.domain.repository.IStudentRepository;
#RestController
#RequestMapping(value = "/test")
public class TesteController2 {
#Autowired
private IStudentRepository iStudentRepository;
#ResponseStatus(HttpStatus.ACCEPTED)
#GetMapping
public List<StudentModel> findCustom2(String name) {
return iStudentRepository.findCustom(name);
}
}
PS: I have a Service Layer com.wizardry.witchcraft.domain.service.RegisterStudentService however
the method in question does not go through it(yet!) because I'm testing, I tried to pass by the service and see what happen but give the same ERROR.
*
ERROR: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'studentController': Unsatisfied dependency expressed through field 'registerStudentService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'registerStudentService': Unsatisfied dependency expressed through field 'iStudentRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'IStudentRepository' defined in com.wizardry.witchcraft.domain.repository.IStudentRepository defined in #EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Failed to create query for method public abstract java.util.List com.wizardry.witchcraft.domain.repository.ICustomRepository.findCustom(java.lang.String)! No property findCustom found for type StudentModel!
*
Thanks in Advance! I'm really lost here.

Just rename your class CustomRepositoryImpl to IStudentRepositoryImpl and it should work.
#Repository
public class IStudentRepositoryImpl implements ICustomRepository {
#PersistenceContext
private EntityManager manager;
#Override
public List<StudentModel> findCustom (String name){
//METHOD
}
}
Below is the documentation from Spring
Configuration If you use namespace configuration, the repository
infrastructure tries to autodetect custom implementations by scanning
for classes below the package we found a repository in. These classes
need to follow the naming convention of appending the namespace
element's attribute repository-impl-postfix to the found repository
interface name. This postfix defaults to Impl.
https://docs.spring.io/spring-data/data-commons/docs/current-SNAPSHOT/reference/html/#repositories.custom-implementations

Related

spring boot constructor parameter could not be found

I am working on spring boot app with tutorial. I did everything like guy from tutorial but still have problem with some constructor:(
The error is:
Parameter 0 of constructor in com.wewtorek.shop.controllers.AdminController required a bean of type 'com.wewtorek.shop.models.data.PageRepository' that could not be found.
Code is:
package com.wewtorek.shop.controllers;
import com.wewtorek.shop.models.data.Page;
import com.wewtorek.shop.models.data.PageRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
#Controller
#RequestMapping("/admin")
public class AdminController {
private PageRepository pageRepository;
public AdminController(PageRepository pageRepository) {
this.pageRepository = pageRepository;
}
#GetMapping
public String admin(Model model) {
List<Page> pages = pageRepository.findAll();
model.addAttribute("pages", pages);
return "admin";
}
}
PageRepository:
package com.wewtorek.shop.models.data;
import org.springframework.data.jpa.repository.JpaRepository;
public interface PageRepository extends JpaRepository<Page, Integer> {
}
Application:
package com.wewtorek.shop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class ShopApplication {
public static void main(String[] args) {
SpringApplication.run(ShopApplication.class, args);
}
}
First :
#Repository is missing
#Repository
public interface PageRepository extends JpaRepository<Page, Integer> {
}
Doc : https://www.baeldung.com/spring-data-repositories
You dont have to create an constructor in controller :
It should be something like this :
public class AdminController{
#Autowired
private PageRepository pageRepository;
--- Code ---
}
#Autowired instanciate a service, you dont have to build it
BUT you have to put #Repository or #Service to use #Autowired
I take this example from my school project :
Controller
In my LoanService i call another server but u can replace it by u'r repository
Service
And last tips i promise :D, a complete NoSQL school project i did
https://github.com/juju630/ClientServeurNoSQL
( sry not native )
Without looking at your project, this is going to be hard to give a definitive solution.
What is happening is when spring tries to create the bean AdminController it can not find a unique bean as the dependency PageRepository.
A few things to look at to try to solve this
Is the bean JpaRepository<Page, Integer> annotated correctly for spring to pick it up and create an instance?
Is the bean JpaRepository<Page, Integer> being scanned by spring?
What is your package structure? this can be very important for the default scanning of spring beans.
To investigate you could add a default constructor to allow it to ignore the dependency, then debug out all beans on startup of your app using the answers
Here
I hope this helps.

Spring Boot Application can not inject Bean from another module

I have the following 3 modules in my spring-boot application:
web (Entry point / Main Application class annotated with #SpringBootApplication
persistence
service
I'm now trying to inject a service in the web module which comes from the service. In the service I'm injecting the repository which comes from the persistence module. When I start the application the following error shows up:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.project.service.images.ImageService required a bean of type 'com.project.persistence.repositories.ImageRepository' that could not be found.
Action:
Consider defining a bean of type 'com.project.persistence.repositories.ImageRepository' in your configuration.
ImageService class:
package com.project.service.images;
import com.project.common.entities.Image;
import com.project.persistence.repositories.ImageRepository;
import com.project.service.AbstractService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.persistence.EntityNotFoundException;
import java.util.Date;
import java.util.List;
#Component
public class ImageService extends AbstractService {
private final ImageRepository imageRepository;
#Autowired
public ImageService(ImageRepository imageRepository) {
this.imageRepository = imageRepository;
}
public Image getImage(Long id) {
return imageRepository.findById(id).orElseThrow(EntityNotFoundException::new);
}
public List<Image> getAll() {
return imageRepository.findAll();
}
public List<Image> getAll(Date from) {
return imageRepository.findByDateRange(from, null);
}
public List<Image> getAll(Date from, Date to) {
return imageRepository.findByDateRange(from, to);
}
public List<Image> getAllForDay(Date day) {
return imageRepository.findAll();
}
}
ImageRepository class:
package com.project.persistence.repositories;
import com.project.common.entities.Image;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.Date;
import java.util.List;
#Repository
public interface ImageRepository extends JpaRepository<Image, Long> {
#Query("SELECT i FROM Image i WHERE i.created > :from AND i.created < :to")
public List<Image> findByDateRange(#Param("from") Date from, #Param("to") Date to);
}
And that's how I inject the service into my class in the web module:
#Autowired
private ImageService imageService;
So on I was searching throught the internet and saw some people with similar problems. Then I got the tip that I should add the scanBasePackages to the SpringBootApplication annotation at my application class. So I did this:
package com.project.web;
#SpringBootApplication(scanBasePackages = "com.project.service")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
But it's still not working. If I add the specific package for scanning to the annotation com.project.service.images the injection of the ImageService works but then it can't find the ImageRepository in it.
What am I doing wrong?
I know that so many modules doesn't make sense for such a small application but I have to because it's for my apprenticeship and we need to make multiple modules.
What normally should do is to have this structure in your app
app
SpringBootApp.java
app.repositories
Repository.java
app.services
Service.java
If you are not following that package structure, then you need to have
#EnableJpaRepositories
And watch out for your entities which may have the same issue, in that case take a look at:
#EntityScan
Just try to change scanBasePackages to "com.project". Repository is in a different package.
eg:
#SpringBootApplication(scanBasePackages = "com.project")
Spring is not able to scan your repository class as it resides in different package.
As per your response in comments, your Application class in under
com.project.web
, so by default Spring will scan all classes under this packages and subpackages. So you need to put all your spring components under the same package/sub package where your application resides.
Create a config class , and define all you beans in that one location , in this case you need the bean for ImageRepository, Something like...
#Configuration
#ComponentScan
public class Config {
#Bean
public ImageRepository getImageRepository() {
// return the image repository object
}
}

Field in required a bean of type that could not be found consider defining a bean of type in your configuration

I'm getting the following error when trying to run my app:
Field edao in com.alon.service.EmployeeServiceImpl required a bean of
type 'com.alon.repository.EmployeeRepository' 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.alon.repository.EmployeeRepository' in your configuration.
Project structure:
EmployeeRepository:
package com.alon.repository;
import com.alon.model.Employee;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
public interface EmployeeRepository {
List<Employee> findByDesignation(String designation);
void saveAll(List<Employee> employees);
Iterable<Employee> findAll();
}
EmployeeServiceImpl:
package com.alon.service;
import com.alon.model.Employee;
import com.alon.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
#Service
public class EmployeeServiceImpl implements EmployeeService {
#Autowired
private EmployeeRepository edao;
#Override
public void saveEmployee(List<Employee> employees) {
edao.saveAll(employees);
}
#Override
public Iterable<Employee> findAllEmployees() {
return edao.findAll();
}
#Override
public List<Employee> findByDesignation(String designation) {
return edao.findByDesignation(designation);
}
}
MyApplication:
package com.alon;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class MyApplicataion {
public static void main(String[] args) {
SpringApplication.run(MyApplicataion.class, args);
}
}
As you have added spring-boot tag I guess you are using sprig data jpa. Your repository interfaces should extend org.springframework.data.repository.Repository (a marker interface) or one of its sub interfaces (usually org.springframework.data.repository.CrudRepository) for instructing spring to provide a runtime implementation of your repository, if any of those interfaces are not extened you'll get
bean of type 'com.alon.repository.EmployeeRepository' that could not
be found.
I assume you try to use spring data JPA. What you can check / debug is:
Is JpaRepositoriesAutoConfiguration executed? You can see this in the start up log in the debug log level
Does something change if you addionally add #EnableJpaRepositories with the corresponding basepackages.
Add #ComponentScan with the corresponding packages, normally #SpringBootApplication should do it, but just in case.
you can also check the autconfig documentation: https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-auto-configuration.html
EDIT: see comment from #ali4j: I did not see that it is the generic spring Repository interface and not the spring data interface
regards,WiPu

Constructor injection not work with qualifiers in #Statless bean

I encountered the next exciting problem. If I try to run the next code on Weblogic 12.2.1.3.0:
package hu.sample.bean;
import lombok.extern.slf4j.Slf4j;
import javax.ejb.Stateless;
import javax.inject.Inject;
import java.util.Random;
#Slf4j
#Stateless
public class Salutatory {
private Butler butler;
private Butler assistant;
public Salutatory() {}
#Inject
public Salutatory(#MainButler Butler mainButler,
#Assistant Butler assistant) {
this.butler = mainButler;
this.assistant = assistant;
log.debug("Call with {} {}", mainButler, assistant);
}
public String salute(String name) {
if (new Random().nextBoolean()) {
return butler.welcome(name + "1");
} else {
return assistant.welcome(name + "2");
}
}
}
package hu.sample.bean;
public interface Butler {
String welcome(String name);
}
package hu.sample.bean;
import javax.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface MainButler {
}
package hu.sample.bean;
import javax.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface Assistant {
}
package hu.sample.bean;
import javax.ejb.Stateless;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.Any;
import javax.inject.Named;
#Stateless
#MainButler
public class George implements Butler {
#Override
public String welcome(String name) {
return String.format("Hello, I'm Geroge, I'm the main butler. Welcom here %s! (%s)", name, this.getClass());
}
}
package hu.sample.bean;
import javax.ejb.Stateless;
#Stateless
#Assistant
public class Peter implements Butler {
#Override
public String welcome(String name) {
return String.format("Hello, I'm Peter, I'm an assistant. Welcom here %s! (%s)", name, this.getClass());
}
}
I get the next exception.:
weblogic.management.DeploymentException: CDI deployment failure:WELD-001408: Unsatisfied dependencies for type Butler with qualifiers #Default
at injection point [BackedAnnotatedParameter] Parameter 1 of [BackedAnnotatedConstructor] public hu.sample.bean.Salutatory_mv77ws_Impl(Butler, Butler)
at hu.sample.bean.Salutatory_mv77ws_Impl.<init>(Salutatory_mv77ws_Impl.java:0)
WELD-001475: The following beans match by type, but none have matching qualifiers:
- Session bean [class hu.sample.bean.Peter with qualifiers [#Assistant #Any]; local interfaces are [Butler],
- Session bean [class hu.sample.bean.George with qualifiers [#MainButler #Any]; local interfaces are [Butler]
Ok, I understand this and I create a default implementation of Butler. But in this case the CDI injects default implementation every time, ignored the qualifiers. Why?
The qualifiers works well, if I use setter injection, field injection, etc...
If Salutatory is #WebServlet the constructor injection with qualifiers also works well.
But, why don't constructor injection with qualifiers works well in #Stateless bean?
Many thanks for your answers!
To inject an EJB it's best to use the javax.ejb.EJB annotation in an EJB injection context. (i.e. the injection point is an EJB itself)
In this case, you can use the simple name of the class as the beanName to choose which implementation to inject.
In your case, instead of qualifying the beans in the constructor, do so for each field.
#EJB(beanName="George")
private Butler butler;
#EJB(beanName="Peter")
private Butler assistant;

Autowiring for #Service field failed

I keep looking at my codes all day along, I can't find the cause. The repository (#Repository) is working just fine, it's the #Service field that I keep failing get it autowired, I keep struggling since everything looks fine
The dispatched servlet:
<mvc:annotation-driven />
<context:component-scan base-package="com" />
com.repository
Repo.java
package com.repository;
import com.domain.Student;
import java.util.List;
public interface Repo { public List<Student> getAllSiswa(); }
RepoImplement.java
import com.domain.Student;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
#Repository
public class RepoImplement implements Repo {
List<Student> ls = new ArrayList<>();
public RepoImplement(){
Student s1 = new Student();
s1.setNama("paul");
Student s2 = new Student();
s2.setNama("robert");
ls.add(s1);
ls.add(s2);
}
#Override
public List<Student> getAllSiswa() {
return this.ls;
}
}
package com.service;
Serve.java
import com.domain.Student;
import java.util.List;
public interface Serve {
void changeName(String namaBaru);
public List<Student> newList();
}
I'm suspecting there's something wrong over here
ServeImplement.java
import com.domain.Student;
import com.repository.Repo;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class ServeImplement implements Serve {
#Autowired
public Repo repo;
List<Student> s = repo.getAllSiswa(); // THIS IS SUSPECTING ME.
#Override
public void changeName(String namaBaru) {
s.get(0).setNama(namaBaru); // get first Student, then update its name.
}
#Override
public List<Student> newList() {
return this.s;
}
}
controller2.java (it's the mapping request for serving student)
package com.controlller;
import com.domain.Student;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.service.Serve;
#Controller
public class controller2 {
#Autowired
public Serve serv;
#RequestMapping("/changename")
public ModelAndView sdaf() {
serv.changeName("New name");
List<Student> list = serv.newList();
return new ModelAndView("page2", "out", list);
}
}
The error:
Error creating bean with name 'controller2': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: public com.service.Serve
com.controlller.controller2.serv; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'serveImplement' defined in file
[C:\Users\hans\Documents\NetBeansProjects\WebApplication2\build\web\WEB-INF\classes\com\service\ServeImplement.class]:
Instantiation of bean failed; nested exception is
org.springframework.beans.BeanInstantiationException: Could not
instantiate bean class [com.service.ServeImplement]: Constructor threw
exception; nested exception is java.lang.NullPointerException
You need to understand how autowiring, or in fact Java in general, works.
Spring must create an instance of your ServImpl class, and populate the field repo with the repository bean. It does that using reflection, but what it does is basically equivalent to the following code:
ServImpl s = new ServImpl();
s.repo = theRepoBean;
So, you see that s.repo becomes non-null after the constructor has been executed. And, while executing the constructor, the following line of code is executed:
List<Student> s = repo.getAllSiswa();
At that moment, repo hasn't been initialized yet. So it's null. So you get a NullPointerException.
Use constructor injection instead of field injection, or use a #PostConstruct annotated method.
And please, make your fields private instead of public.
That said, the goal of a repository is normally to get data from a database. And the students can be modified,deleted or created in the database. So initializing a field of your service and returning always the same list defeats the whole point of having a repo. You should call repo.getAllSiswa() from newList(), every time it's called, to get the latest, up-to-date, list of students.
Because Autowired on field happens right after construction, you need to change your code in order to get it to work.
This should works:
#Service
public class ServeImplement implements Serve {
public Repo repo;
List<Student> s;
#Autowired
public ServeImplement(Repo repo) {
this.repo = repo;
s = repo.getAllSiswa();
}
#Override
public void changeName(String namaBaru) {
s.get(0).setNama(namaBaru); // get first Student, then update its name.
}
#Override
public List<Student> newList() {
return this.s;
}
}
Moreover, using Autorwired annotation on constructor allow you to mark your field as final if the instance shouldn't have to change.

Categories

Resources