So i'm trying to connect java spring to mongoDB and using a findById, but it always gives the null pointer error.
2020-08-04 13:54:01.893 ERROR 8312 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at pt.project.ProvaConceito_BackEnd.mongoDB.UserService.findById(UserService.java:29) ~[classes/:na]
at pt.project.ProvaConceito_BackEnd.mongoDB.mongoDBService.getUserByID(mongoDBService.java:19) ~[classes/:na]
The structure of this project is:
Java
MongoDB
mongoDBService
UserService
Pojos
User
Repositories
UserRepository
I'm gonna share the code I have right now:
mongoDBService
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import pt.project.ProvaConceito_BackEnd.pojos.User;
#RestController
#CrossOrigin(origins="http://localhost:4200")
public class mongoDBService {
UserService userService = new UserService();
#RequestMapping("/concept/user")
public User getUserByID(Integer id) {
return userService.findById(1);
}
}
UserService
package pt.project.ProvaConceito_BackEnd.mongoDB;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pt.project.ProvaConceito_BackEnd.pojos.User;
import pt.project.ProvaConceito_BackEnd.repositories.UserRepository;
import java.util.List;
#Service
public class UserService {
#Autowired(required = false)
private UserRepository userRepository;
public void save(String nome, int idade, String morada) {
userRepository.save(new User(nome, idade, morada));
}
public List<User> findAll() {
return userRepository.findAll();
}
public long count() {
return userRepository.count();
}
public User findById(Integer id) {
return userRepository.findById(id).orElse(null);
}
public void delete(Integer id) {
userRepository.deleteById(id);
}
}
Users (Pojo)
package pt.project.ProvaConceito_BackEnd.pojos;
import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document(collection = "Users")
#Getter
public class User {
#Id
private Integer id;
private String nome;
private int idade;
private String morada;
public User(String nome, int idade, String morada) {
this.nome = nome;
this.idade = idade;
this.morada = morada;
}
}
UserRepository
package pt.project.ProvaConceito_BackEnd.repositories;
import org.springframework.data.mongodb.repository.MongoRepository;
import pt.project.ProvaConceito_BackEnd.pojos.User;
public interface UserRepository extends MongoRepository<User, Integer> {
}
What am I doing wrong here? I think the problem is on mongoDBService...
in this line:
UserService userService = new UserService();
Because I think that it's not being injected, but I don't know how to solve that...
EDIT 1
I have my main class inside pt.project.ProvaConceito_BackEnd:
ProvaConceitoBackEndApplication
package pt.project.ProvaConceito_BackEnd;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class ProvaConceitoBackEndApplication {
public static void main(String[] args) {
SpringApplication.run(ProvaConceitoBackEndApplication.class, args);
}
}
Do not use new to create object that way because spring will not have any knowledge of that object and will not be able to inject it.
Since you have already annotated the UserService with #service you should use #Autowired annotation
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import pt.project.ProvaConceito_BackEnd.pojos.User;
#RestController
#CrossOrigin(origins="http://localhost:4200")
public class mongoDBService {
#Autowired
private UserService userService;
#RequestMapping("/baieuropa/user")
public User getUserByID(Integer id) {
return userService.findById(1);
}
}
Also annotate the UserRepository class with #Repository
package pt.project.ProvaConceito_BackEnd.repositories;
import org.springframework.data.mongodb.repository.MongoRepository;
import pt.project.ProvaConceito_BackEnd.pojos.User;
#Repository
public interface UserRepository extends MongoRepository<User, Integer> {
}
Remove required = false from UserService class
Make sure to have a class with #SpringBootApplication annotation in pt.project.ProvaConceito_BackEnd package.
You need to update your repository and User.class to have an ID type of string instead of Integer. This is required for mongo repositories. May not completely solve your problem but will be a step in the right direction
#Repository
public interface UserRepository extends MongoRepository<User, String> {
}
I would follow the suggestions of other contributors to remove the "new" keyword from your mongoDBService and use the #Repository annotation too.
Autowiring only works if all components that it relies on are autowired too. So in your case because the mongoDBService uses new for the UserService then it expects the Repository to use "new" too. You should instead autowire at all levels and remove the required=false from the autowiring in the UserService.
The repository requires the #Repository annotation otherwise when Spring does its component scan the repository wont be picked up without it. #Repository #RestController etc are all just stereotypes for #Component with varying degrees of additional functionality
Related
This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 1 year ago.
this is a part of TopicService.java
I get a NULL pointer exception at this line
TopicRepo.findAll().forEach(topics::add);
private topicRepo TopicRepo; this is where I am auto wiring the dependency
package io.javabrains.example.topic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
#Service
public class TopicService {// creating a business service
#Autowired
private topicRepo TopicRepo; // whenever the TopicService creates an instance then the instance of topicRepo will be injectjed in the variable
private ArrayList<Topic> topics = new ArrayList<>(Arrays.asList(
new Topic("spring1", "nishhcal", "name"),
new Topic("spring2", "nishhca2", "name2"),
new Topic("spring3", "nishhca3", "name3")));
public List<Topic> getAllTopics(){
// return topics;
List<Topic> topics = new ArrayList<>();
TopicRepo.findAll().forEach(topics::add);
return topics;
}
public Topic getSpecificTopic(String id){
// for (int r=0; r < topics.size(); r++){
//
// if((topics.get(r).getId()).equals(id)){
//
// return topics.get(r);
// }
// }
// return topics.stream().filter(t -> t.getId().equals(id)).findFirst().get();// alternative way of doing it
return topics.get(0);
}
public void addTopic(Topic topic){
// topics.add(topic);
TopicRepo.save(topic);
}
topicRepo.java, this is where I am extending the crud repository
package io.javabrains.example.topic;
import org.springframework.data.repository.CrudRepository;
public interface topicRepo extends CrudRepository<Topic, String> {
}
and this is what is have in my application.properties file
#JPA
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database=default
spring.jpa.show-sql=true
here is a part of my Topic.java
package io.javabrains.example.topic;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class Topic {
#Id
private String id;
private String name;
private String description;
public Topic() {
}
Is Topic class annotated with JPA #Entity and have #Id on String field ?
Note: Try to declare Class/Interface names in Capitalized format.
Add #Repository annotation in your topicRepo interface :
#Repository
public interface topicRepo extends CrudRepository<Topic, String> {
If I remember well you still have to create the functions/methods in the #Repository file that you need as below example from docs. Also try to keep the syntax for classes to start with UpperCase letter TopicRepo instead of topicRepo and objects with lower case topicReport instead of TopicRepo:
#Autowired
TopicRepo topicReport
and finally your repo interface:
#Repository
public interface TopicRepo extends CrudRepository<Topic, Long> {
List<Topic> findAll();
}
When I start my spring-boot app I have this message:
APPLICATION FAILED TO START
Description:
Field userDAO in com.gisapp.services.impl.UserService required a bean
of type 'com.gisapp.gisapp.dao.IUserDAO' 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.gisapp.gisapp.dao.IUserDAO' in
your configuration.
What I have read in other post related to this problem is that I have to configure the annotation #ComponentScan, but it does not work
MAIN CLASS:
package com.gisapp.gisapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
#ComponentScan("com.gisapp")
public class GisappApplication {
public static void main(String[] args) {
SpringApplication.run(GisappApplication.class, args);
}
}
SERVICE CLASS
#Service
public class UserService implements IUserService {
#Autowired
IUserDAO userDAO;
#Override
#Transactional(readOnly=true)
public Object login() {
return userDAO.login();
}
}
- UserDAO
package com.gisapp.gisapp.dao.impl;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import com.gisapp.gisapp.dao.IUserDAO;
import com.gisapp.models.entity.User;
public class UserDAO implements IUserDAO{
#Override
public Object login() {
StringBuilder query = new StringBuilder();
query.append("SELECT * FROM User");
EntityManager em = null;
Query q = em.createNativeQuery(query.toString());
List<User> result=q.getResultList();
return result;
}
}
IUserDAO should be recognised as a bean and the app should run
1) Add a #Repository annotation in order for the DAO to be loaded as a bean into the spring context:
#Repository
public class UserDAO implements IUserDAO{
2) Just on the side.. you should most likely inject the EntityManager in it also:
#PersistenceContext
private EntityManager em;
I'm trying to create a bean of the DatastoreRepository class but I get the following error Iam using spring-boot 2.1.3
Description:
The bean 'bookRepository', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
this is my project structure I have the Application main run class in a root package like this
com.mycompany.project
--Application.java
--controller
--domain
--repository
The class with the #SpringBootApplication is in the root package
here is my repository class
import org.springframework.cloud.gcp.data.datastore.repository.DatastoreRepository;
public interface BookRepository extends DatastoreRepository<Book, Long>{
}
Here is my domain class
import org.springframework.cloud.gcp.data.datastore.core.mapping.Entity;
import org.springframework.data.annotation.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#Entity(name = "books")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Book {
#Id
Long id;
String title;
}
and here is my controller class
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class AppUserController {
#Autowired
BookRepository bookRepository;
#GetMapping("/booksave")
public String helloworld() {
bookRepository.save(new Book(3L, "author"));
return "book saved";
}
}
and here is my Application class
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I think the problem is the way you are using the annotation, try changing the Injection to the Constructor, something like:
#RestController
public class AppUserController {
private BookRepository bookRepository;
#Autowired
public AppUserController (
BookRepository bookRepository){
this.bookRepository= bookRepository;
}
#GetMapping("/booksave")
public String helloworld() {
bookRepository.save(new Book(3L, "author"));
return "book saved";
}
}
Source to understand it: Spring #Autowire on Properties vs Constructor
Add this annotation, #EnableDatastoreRepositories, to your Application.java
How about using Spring data rest :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
you won't need to code controllers
import org.springframework.cloud.gcp.data.datastore.repository.DatastoreRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
#RepositoryRestResource(collectionResourceRel = "xxxxxs", path = "xxxx")
public interface XXXXXRepository extends DatastoreRepository<XXXXX, String>
swagger config !!!
#Configuration
#EnableSwagger2WebMvc
#Import(SpringDataRestConfiguration.class)
public class SwaggerConfig {
So i've been trying to add some new data in another class which i create myself (not in the default class below the Controller)
so here's the code to explain it:
MainController.java
package hello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import hello.User;
import hello.UserRepository;
#Controller
#RequestMapping(path="/demo")
public class MainController {
#Autowired
private UserRepository userRepository;
#GetMapping(path="/add")
public #ResponseBody String addNewUser () {
pge j = new pge();
j.pgl(); //here's the code to add data in mysql
return "Saved";
}
}
and here's the code in my pge.java
package hello;
import org.springframework.beans.factory.annotation.Autowired;
public class pge {
#Autowired
private UserRepository userRepository;
public void pgl() {
User n = new User();
n.setName("sarah");
n.setEmail("sarah#gmail.com");
userRepository.save(n);
}
}
everytime i open localhost:8080/demo/add , in web it just gives whitelabel error page and in java it gives null error. all i want to do is just adding new data in my database (MySQL) in my own class.
As pge object not managed by Spring, autowired annotation will not inject UserRepository bean. I will suggest changing pge to spring managed bean.
#Service
public class pge {
#Autowired
private UserRepository userRepository;
...
}
And in Main Controller
#Controller
#RequestMapping(path="/demo")
public class MainController {
#Autowired
private UserRepository userRepository;
#Autowired
private pge pge;
#GetMapping(path="/add")
public #ResponseBody String addNewUser () {
pge.pgl(); //here's the code to add data in mysql
return "Saved";
}
}
I started to develop APIs for iOS app using Java Spring MVC two months ago.
I'll explain my question with an example for clarification purpose.
Let's say I want to update a user's name.
My mySQL user table has columns: id, user_id, email, display_name.
My approach was:
define user:
User.java:
package bean;
public class User {
String displayName;
String email;
String userId;
getters/setters...
}
2.define a DAO:
UserDao.java:
package dao;
import bean.StandardResponse;
public interface UserDao {
public StandardResponse updateUserName(String user_id,String userName);
}
UserDaoImpl.java:
package implement;
import bean.User;
import common.DatabaseConnect;
public UserDaoImpl implements UserDao {
private DatabaseConnect dbConnect;
public UserDaoImpl(DatabaseConnect dbConnect) {
this.dbConnect = dbConnect;
}
public StandardResponse updateUserName(userId,userName) {
if ((userId == null || userId.isEmpty()) ||(userName == null || userName.isEmpty())) return new StandardResponse("Error","Parameters not set!");
String sql = null;
Statement smt = dbConnect.createDataBaseConnectResourse();
StandardResponse result = new StandardResponse("Error","Fail to update the record!");
sql = "update User set user_name="+userName+" where user_id='"+userId+"'";
int updateResult = 0;
try {
updateResult = smt.executeUpdate(sql);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
dbConnect.closeDatabaseConnectResource();
}
if (updateResult == 1) {
result = new StandardResponse("Success","The record has been updated!");
}
return result;
}
}
3.controller
import org.springframework.stereotype.Controller;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import bean.StandardResponse;
import bean.User;
import common.DatabaseConnect;
import common.SpringApplicationContext;
import dao.UserDao;
import implement.UserDAOImpl;
#Controller
#RestController
#RequestMapping("user")
public class UserController {
DatabaseConnect dbConnect = SpringApplicationContext.getApplicationContext().getBean("databaseConnect", DatabaseConnect.class);
UserDao uiObject = new UserDaoImpl(dbConnect);
#RequestMapping(value = "/updateUserName", method = RequestMethod.POST)
public StandardResponse updateUserName(HttpServletRequest request, HttpServletResponse reponses, Model model) {
StandardResponse srObject = uiObject.updateUserName(request.getparameter("userId"),request.getParameter("userName"));
return srObject;
}
}
I just put the crucial classes here. I believe you can understand what I am doing here. So if someone access the URL:****/user/updateUserName providing the userId and userName, he can update the user name of that record. It is functionalbe.
I used the same approach and finished the whole project. It is working. Then, I asked an experienced programmer to look at my code since I figured out all these based on my own understanding. I would like to know how did they do in industry. He gave me some valuable comments.
The whole structure is wrong. I shouldn't have logics in my dao. I should at least have dao, service and action layers. dao only handles database access, service handles all the logic and action handels communication and decide which service to call.
It is very bad approach to hand written SQL in the code. I should use Hibernate.
I should use control inverse and dependency injection.(I am not dealing with this in this post.)
So I started to update my codes to use Hibernate.
define user:
User.java:
package model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="User")
public class User {
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name = "user_id")
private String userId;
#Column(name = "user_name")
private String displayName;
#Column(name = "email")
private String emai;
all getters/setters...
}
dao:
UserDao.java:
package dao;
import model.User;
import java.util.List;
public interface UserDAO {
public void updateUser(User p);
other methods....
}
UserDaoImpl.java
package dao;
import model.User;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Repository;
import model.User;
#Repository
public class PersonDAOImpl implements PersonDAO {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf){
this.sessionFactory = sf;
}
#Override
public void updatePerson(Person p) {
Session session = this.sessionFactory.getCurrentSession();
session.update(p);
}
implementations of other methods....
}
service:
UserService.java:
package service;
import java.util.List;
import model.User;
public interface UserService {
public void updateUser(User p);
other methods....
}
UserServiceImpl.java:
package service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import dao.UserDAO;
import model.User;
#Service
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
#Override
#Transactional
public void updateUser(User p) {
this.userDAO.updateUser(p);
}
implementation of other methods...
}
Now, I just need to write a class to handle the request and call this updateUser service. The whole structure looks better than mine. However, the request won't gave me the whole user object. All I can get is user_id and user_name. I am also not allowed to put logics in dao. So I have to query the table first to get the whole user object and then update the user_name. Comparing to what I used to do, one SQL handles the update. Now using Hibernate, I need 1 query and 1 update.
I found a solution on StackOverflow that I can use HQL to do this in one step. But I though the purpose for using Hibernate is to free us from writing the SQL. If I need to write HQL, why don't I just keep using the writing SQL approach.
So is there a way to do update with Hibernate without query the table first in this structure? Or this is a trade-off for better structure?
I am sorry that I used a really long example for this question. But I couldn't think of other ways to explain the whole story clearly.
So is there a way to do update with Hibernate without query the table first in this structure? Or this is a trade-off for better structure?
Thereis NO tradeoff, you can do updates with HQL(Hibernate Query Language) as well like how you do in SQL.
You can look at the following code:
UserDAOImpl class:
#Repository
//you are calling this in ur code as PersonDAOImpl..change it to UserDAOImpl
public class UserDAOImpl implements UserDAO {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf){
this.sessionFactory = sf;
}
#Override
public int updateUser(String userId, String userName) {
Session session = this.sessionFactory.getCurrentSession();
Query query = session.createQuery("update User set userName =:userName where userId = :userName ");
query.setString("userName", userName);
query.setString(userName, userName);
int result = query.executeUpdate();
return result;
}
}
UserServiceImpl class:
#Service
public class UserServiceImpl implements UserService {
private UserDAO userDAO;
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
#Override
#Transactional
public void updateUserName(String userId, String userName) {
if(userId !=null && userName != null) {
int result = this.userDAO.updateUser(userId, userName);
if(result==0) //userid not available {
//if userid is NOT available, what to do? check your requirement and handle properly
}
}
} else {
//throw exception
}
}
implementation of other methods...
}
Controller Class:
#Controller
#RestController
#RequestMapping("user")
public class UserController {
#Autowired
private UserService userService;
#RequestMapping(value = "/updateUserName", method = RequestMethod.POST)
public StandardResponse updateUserName(HttpServletRequest request, HttpServletResponse reponses, Model model) {
StandardResponse srObject = userService.updateUserName(request.getparameter("userId"),request.getParameter("userName"));
return srObject;
}
}