Spring Boot Application- Ambiguous mapping Cannot map method - java

i am creating a simple spring boot project when i run the project ran into the problem with Spring Boot Ambiguous mapping. Cannot map method. i have create two various controller those are student and course controller.i completly attached the error below.
i attached the full
**gub link here** https://github.com/raguram1986/SpringSecuritys
Full Error i attached below
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'studentController' method
com.example.studentmanagement.Controller.StudentController#saveStudent(Student)
to {POST [/save]}: There is already 'courseController' bean method
com.example.studentmanagement.Controller.CourseController#saveCourse(Course) mapped.
Controller
#Controller
public class StudentController {
#Autowired
private StudentService service;
#GetMapping("/Student")
public String viewHomePage(Model model) {
List<Student> liststudent = service.listAll();
// model.addAttribute("liststudent", liststudent);
System.out.print("Get / ");
return "Student";
}
#GetMapping("/addStudent")
public String add(Model model) {
List<Student> liststudent = service.listAll();
model.addAttribute("liststudent", liststudent);
model.addAttribute("student", new Student());
return "addstudent";
}
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String saveStudent(#ModelAttribute("student") Student std) {
service.save(std);
return "Student";
}
#RequestMapping("/edit/{id}")
public ModelAndView showEditStudentPage(#PathVariable(name = "id") int id) {
ModelAndView mav = new ModelAndView("addstudent");
Student std = service.get(id);
mav.addObject("student", std);
return mav;
}
#RequestMapping("/delete/{id}")
public String deleteStudentPage(#PathVariable(name = "id") int id) {
service.delete(id);
return "Student";
}
Student
#Entity
public class Student {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String stname;
private String course;
private int fee;
public Student() {
}
public Student(Long id, String stname, String course, int fee) {
this.id = id;
this.stname = stname;
this.course = course;
this.fee = fee;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getStname() {
return stname;
}
public void setStname(String stname) {
this.stname = stname;
}
public String getCourse() {
return course;
}
public void setCourse(String course) {
this.course = course;
}
public int getFee() {
return fee;
}
public void setFee(int fee) {
this.fee = fee;
}
StudentRepository
#Repository
public interface StudentRepository extends JpaRepository<Student, Long>{
}
Service
#Service
public class StudentService
{
#Autowired
private StudentRepository repo;
public List<Student> listAll() {
return repo.findAll();
}
public void save(Student std) {
repo.save(std);
}
public Student get(long id) {
return repo.findById(id).get();
}
public void delete(long id) {
repo.deleteById(id);
}
}

In your StudentController you have the endpoint /save
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String saveStudent(#ModelAttribute("student") Student std) {
service.save(std);
return "Student";
}
But you haven't included the CourseController class in your question, which is mentioned in the error.
If you have defined an endpoint /save in that CourseController, then you have to rename it. Otherwise, when you invoke /save which controller needs to be invoked cannot be determined.

Add #RequestMapping above StudentController as below
#Controller
#RequestMapping("/students")
public class StudentController {
...
}
You are getting exception because there is already a mapping defined for path
/save
without being any controller mapping, so the first with root mapping is considered, but the next time it encounters same mapping it is already registered hence it is complaining. For clarity i'd suggest to add #RequestMapping to CourseController as well.
So now your course controller also becomes:
#Controller
#RequestMapping("/courses")
public class CourseController {
...
}

The best practice is to always add a request mapping at class level as well, like in your case, add a mapping like "/students" for SutdentController and "/course" for CourseController itself and then all other methods will be under that i.e. "/student/save" and then you will not face this issue anymore.

Related

unit Test cases for spring boot application

I have written 1 unit Test for 1 single public method and need help from other methods of customer controller which I can refer to and write for other controllers and services.
CustomerController
#CrossOrigin(origins = "http://localhost:4200")
#RestController
#RequestMapping("/api/v1/")
public class CustomerController {
private static final Logger log = LoggerFactory.getLogger(CustomerController.class);
#Autowired
private CustomerRepository customerRepository;
public List<Customer> getAllcustomers(){
return customerRepository.findAll();
}
**public Customer createcustomer(#RequestBody Customer customer) {
log.info("inside customer add ***********");
return customerRepository.save(customer);
}**
**public ResponseEntity<Customer> getcustomerById(#PathVariable Long id) {
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("customer not exist with id :" + id));
return ResponseEntity.ok(customer);
}**
**public ResponseEntity<Customer> updatecustomer(#PathVariable Long id, #RequestBody Customer customerDetails){
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("customer not exist with id :" + id));
customer.setFullName(customerDetails.getFullName());
customer.setPhoneNumber(customerDetails.getPhoneNumber());
customer.setPhone2(customerDetails.getPhone2());
customer.setDistrict(customerDetails.getDistrict());
Customer updatedcustomer = customerRepository.save(customer);
return ResponseEntity.ok(updatedcustomer);
}**
**public ResponseEntity<Map<String, Boolean>> deletecustomer(#PathVariable Long id){
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("customer not exist with id :" + id));
customerRepository.delete(customer);
Map<String, Boolean> response = new HashMap<>();
response.put("deleted", Boolean.TRUE);
return ResponseEntity.ok(response);
}**
}
Below is the Customer Model - which I am using
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String fullName;
private int phoneNumber;
private int phone2;
private String email;
private String district;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public int getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(int phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
UnitTest for Customer.
#ExtendWith(MockitoExtension.class)
public class CustomerControllerTest {
#Mock
CustomerRepository customerRepository;
#InjectMocks
CustomerController customerController;
#Test
public void testGetAllcustomers() {
when(customerRepository.findAll()).thenReturn(getCusts());
List<Customer> res = customerController.getAllcustomers();
assertEquals(res.size(),1);
}
public List<Customer> getCusts(){
List<Customer> custs = new ArrayList<>();
Customer c = new Customer();
c.setFullName("Dinga");
custs.add(c);
return custs;
}
}
Like the above Unit Test cases, I need other methods also. Marked in Bold for which I need Unit Test cases.
For testing a rest controller, it's recommended to use mockMvc. This acts like a rest client, but it does not actually start the server. Instead it uses the spring classes to call your code in almost the same way as if it were processing a real HTTP request. MoockMvc will perform the conversion of your data to Json and retrieve a Json result. Here's the official documentation, with some examples on how to use it: springdoc. Using ObjectMapper (or JacksonTester, which uses an ObjectMapper) you can deserialize the respone into objects.
#WebMvcTest(CustomerController.class)
#ComponentScan("com.your.base.package")
class ControllerTest{
#MockBean
private CustomerRepository customerRepository;
#Autowired
private MockMvc mockMvc;
#Test
void test1(){
when(customerRepository.findAll()).thenReturn(<whatever you want>)
// call mockMvc
this.mockMvc.perform(get("/")).andDo(print())
.andExpect(status().isOk());
}
}

Parameter 0 of constructor in .... Spring Boot

I am a fish in Spring Boot and Data Jpa, I Tried to create a basic Spring boot application but every time I am encountering the error. Can you help me?
That's my code:
Spring Boot Application class:
#SpringBootApplication
#ComponentScan(basePackages = "com.project.*")
#EnableJpaRepositories(basePackages = "com.project.repository.*")
#EntityScan(basePackages = "com.project.entities.*")
#EnableAutoConfiguration
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
Controller Class:
#RestController
#RequestMapping(value = "/api")
public class controller {
private IUserServices userServices;
#Autowired
public controller(IUserServices userServices) {
this.userServices = userServices;
}
#GetMapping(value = "/merhaba")
public String sayHello(){
return "Hello World";
}
#GetMapping(value = "/getall")
public List<User> getAll(){
return this.userServices.getAllUsers();
}
}
Repository Class:
#Repository
public interface UserRepository extends JpaRepository<User,Long> {
}
IServices Class:
#Service
public interface IUserServices {
void saveUser(User user);
List<User> getAllUsers();
}
ServicesImpl Class:
#Service
public class UserServicesImpl implements IUserServices{
private UserRepository userRepository;
#Autowired
public UserServicesImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public void saveUser(User user) {
this.userRepository.save(user);
}
#Override
public List<User> getAllUsers() {
return this.userRepository.findAll();
}
}
Entity Class:
#Entity
#Table(catalog = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
public User() {
}
public User(int id, String name) {
this.id = id;
this.name = name;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
#Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
AND THIS MY ERROR MESSAGE:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.project.services.UserServicesImpl required a bean of
type 'com.project.repository.UserRepository' that could not be found.
Action:
Consider defining a bean of type 'com.project.repository.UserRepository' in your
configuration.
Process finished with exit code 0
SO This is application properties file:
spring.jpa.properties.hibernate.dialect =
org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.show-sql=true
spring.datasource.url=jdbc:postgresql://localhost:5432/u
spring.datasource.username=postgres
spring.datasource.password=1234
spring.jpa.properties.javax.persistence.validation.mode = none
There are some issues that you should fix them.
First
When you have the spring boot application with #SpringBootApplication you don't need other stuff such as #EnableAutoConfiguration and etc, So remove them all.
You can read more about it here.
Second
You don't need to annotate your service interface with #Service, because you did it in the UserServicesImpl class.
Third
You defined id as an integer in your user entity but in the repository, you wrote your id as Long. It's wrong. It should be something like this.
#Repository
public interface UserRepository extends JpaRepository<User,Integer> {
}
Try the above solutions and let me know the result.

Unable to create JPA/CRUDRepository bean

I am creating first-time spring rest services with Spring Data JPA.
and getting below error.
APPLICATION FAILED TO START
Description:
Field product_repo in com.example.demo.controller.AddProduct required a bean of type 'com.example.demo.repository.ProductRepositroy' 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.example.demo.repository.ProductRepositroy' in your configuration.
My class and interfaces are :
Controller
#RestController
public class AddProduct {
#Autowired
private ProductRepositroy product_repo;
#GetMapping("/add")
public String addproduct() {
Product p1 = new Product();
p1.setId(1);
p1.setName("Amit");
Product p2 = new Product();
p1.setId(2);
p1.setName("Sumit");
product_repo.save(p1);
product_repo.save(p2);
return "added successfully the recod";
}
}
Entity
#Entity
public class Product {
#Id
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Repository
public interface ProductRepositroy extends CrudRepository<Product, Integer> {
}
Application Test
#SpringBootApplication
public class Demo1Application {
public static void main(String[] args) {
SpringApplication.run(Demo1Application.class, args);
}
}
Add #Repository annotation on ProductRepositroy interface. Also instead of extending CrudRepository extend JpaRepository.
You should use #Repository in the repository to register this class in the bean.
Code should be something like this.
#Repository
public interface ProductRepositroy extends CrudRepository<Product, Integer> {
}
happy coding! :)

How to write Junit test cases for spring boot application?

I have to write some junit test cases to check entity. I'm using postgres as my database.
My entity class
#Entity
#Table(name = "display")
public class Display {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
private String group;
public Display() {
}
public Display(Long id, String title, String grp) {
this.id = id;
this.title= title;
this.group= grp;
}
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return this.id;
}
public void setGroup(String id) {
this.group = id;
}
public String getGroup() {
return this.group;
}
public void settitle(String title) {
this.title = title;
}
public String gettitle() {
return this.title;
}
}
My repository
#Repository
public interface DisplayRepository extends CrudRepository<Display, Long> {
}
Interface
public interface IDisplayService {
List<Display> findAll();
}
Service class
#Service
public class DisplayService implements IDisplayService {
#Autowired
private DisplayRepository repository;
#Override
public List<Display> findAll() {
List<Display> d = (List<Display>) repository.findAll();
return d;
}
}
I tried writing junit test cases but I get Could'nt load Application. Whats the right way to write junit test cases for this?
This is the test case I wrote for service
folder : test/java/example/demo/Test.java
#RunWith(MockitoJUnitRunner.class)
#TestPropertySource("classpath:conn.properties")
public class DisplayServiceTest {
#Value("${id}")
private String value;
#Mock
private DisplayRepository DisplayReps;
#InjectMocks
private DisplayService DisplayService;
#Test
public void whenFindAll_thenReturnProductList() {
Menu m = new Menu()
m.setId(value);
List<Display> expectedDisplay = Arrays.asList(m);
doReturn(expectedDisplay).when(DisplayReps).findAll();
List<Display> actualDisplay = DisplayService.findAll();
assertThat(actualDisplay).isEqualTo(expectedDisplay);
}
in test/java/example/demo/resources
conn.properties
id=2
Its returning 0 for value
Whats the issue?
Thanks
I have managed to make your code to work. I will post only the changed classes:
The interface:
public interface DisplayRepository extends CrudRepository<Display, Long> {
Optional<Display> findByTitle(String name);
}
The test class:
#RunWith(SpringRunner.class)
#AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
#DataJpaTest
public class DisplayRepositoryTest {
#Autowired
private TestEntityManager testEntityManager;
#Autowired
private DisplayRepository productRespository;
#Before()
public void setUp(){
Display m = new Display();
// m.setId(2L); // The ID is autogenerated; can retrieve it from the persistAndFlush result
m.setCategory("Group1");
m.setTitle("Product2");
testEntityManager.persistAndFlush(m);
}
#Test
public void whenFindByName_thenReturnProduct() {
// when
Display product = productRespository.findByTitle("Product2").orElseThrow(() -> new RuntimeException("Product not found"));
// then
assertThat(product.getTitle()).isEqualTo("Product2");
}
#Test
public void whenFindAll_thenReturnProductList() {
// when
List<Display> products = (List<Display>) productRespository.findAll();
// then
assertThat(products).hasSize(1);
}
}
When trying to run the code you provided, there were a few issues:
you were using the reserved word group as a field in the Display class. Because of this, Hibernate couldn't create the table, so I renamed it to category.
there was a compilation issue because the method findByName wasn't defined in the repository; also, there was no field name in the Display class to which the mapping to be made; because of this, I've added the method findByTitle because it's an existing field and it seemed to match the value you queried in the test method.
because the ID field is autogenerated, the test setup() failed when persisting the Display.
If you want to use #Mock for mocking classes, you must call:
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
You can then mock responses as usual: Mockito.when(DisplayReps.findByTitle("A")).thenReturn(Optional.of(new Display(2L, "ALFA", "GRP1")));

Spring MVC passing data between controllers

I'm writing web application using SpringMVC. I would like to keep some data in session until user logout. Earlier I did it by #SessionAttributes("someData"), but then I had to put #ModelAttribute("someData") someDataType someData as argument for every request mapping method. So this is what I did :
My AccessData class :
#Component
#Scope(value = "session")
public class AccessData {
private long userID;
private String userKey;
public AccessData(long ID, String key) {
this.userID = ID;
this.userKey = key;
}
public long getUserID() {
return userID;
}
public void setUserID(long userID) {
this.userID = userID;
}
public String getUserKey() {
return userKey;
}
public void setUserKey(String userKey) {
this.userKey = userKey;
}
}
First controller(here I'm getting and validating user input from form) :
#Controller
#Scope(value = "session")
public class LoginController {
#ModelAttribute("accessData")
public AccessData getAccessData() {
return this.accessData;
}
private Utils utilsService;
private LoginService loginService;
#Autowired
private AccessData accessData;
#Autowired
public LoginController(LoginService loginService, Utils utils) {
this.loginService = loginService;
this.utilsService = utils;
}
#RequestMapping(value = ControllerPaths.LOGIN, method = RequestMethod.POST)
public ModelAndView showLoginStatus(
#ModelAttribute(LOGINDATA) LoginData loginData) {
try {
accessData = loginService.validateLoginData(loginData);
} catch (IncorrectLoginDataException e) {
logger.trace("Showing fail login screen...");
return utilsService.getShowView(ViewPaths.LOGIN_FAIL);
} catch (HTTPException e) {
return utilsService.getShowViewWithStringAttribute(
ViewPaths.INFO_VIEW, MESSAGE, CONNECTION_ERROR);
}
return utilsService.getShowView(ViewPaths.LOGIN_SUCCESS);
}
}
Second controller :
#Controller
#Scope(value = "session")
public class SecondController {
#ModelAttribute("accessData")
public AccessData getAccessData() {
return this.accessData;
}
private Utils utilsService;
private LoginService loginService;
#Autowired
private AccessData accessData;
#Autowired
public SecondController(LoginService loginService, Utils utils) {
this.loginService = loginService;
this.utilsService = utils;
}
#RequestMapping(value = ControllerPaths.SHOW_ACCESS_DATA, method = RequestMethod.GET)
public ModelAndView showAccessData(
System.out.println(accessData.getUserKey());
return utilsService.getShowView(ViewPaths.INDEX);
}
}
The problem is that when I'm printing userKey value in second controller, the value is null. I checked if I'm getting correct data from server in LoginController and it's ok. So what am I doing wrong? Thanks in advance for help
The problem is that you are assigning a new object to the accessData variable. Instead you should update the field of the object that it is already referring to.
accessData = loginService.validateLoginData(loginData);
// Replace above line with something like this. Implement the copyProperties
// method to copy the attributes you need
AccessData newAccessData = loginService.validateLoginData(loginData);
copyPropteries(accessData,newAccessData);
Also it is not required to make the Controllers session scoped, add proxyMode=TARGET_CLASS to the scope annotation of the AccessData class.

Categories

Resources