I'm trying to autowire a CrudRepository in a DTO class but Spring always injects null instead of bean .
My code looks like :
package com.exampleRestApp.task;
import com.exampleRestApp.workers.WorkerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
public class TaskDTO {
private int workerID;
private String name;
boolean finished;
private int taskID;
#Autowired
WorkerRepository w; //w==null in this moment
public Task MapToEntity() {
Task task = new Task();
task.setName(name);
task.setFinished(finished);
task.setTaskID(taskID);
task.setWorker(w.findOne(task.getTaskID()));
return task;
}
And my WorkerRepo :
package com.exampleRestApp.workers;
import java.util.List;
public interface WorkerRepository extends CrudRepository<Worker,Integer> {
List<Worker> findBySurname(String surname);
List<Worker>findAllByOrderByNameAsc();
}
You have to annotate your WorkerRepository with #Repository or #Service spring annotations
#Repository
public interface WorkerRepository extends CrudRepository<Worker,Integer>
Related
The goal of this method is to provide functionality to a service layer method that will find all songs within a PostgreSQL database. I have implemented an interface "SongServiceInterface" in the service layer and in the event I utilize this SongService via instantiation in the main method or even by sending Http Requests via "/songs" endpoint, I receive this error:
Caused by: java.lang.NullPointerException: Cannot invoke "com.techm.react.Wasteland.repository.SongRepo.findAll()" because "this.songRepo" is null.
Please note upon startup the application will persist the objects to database, but using endpoints or methods within this repo/service cause null field error.
I can provide the code below:
Model
package com.techm.react.Wasteland.models;
import lombok.*;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import javax.persistence.*;
import java.sql.Time;
#Entity
#Table(name = "song")
#NoArgsConstructor
#AllArgsConstructor
#Getter #Setter
#EqualsAndHashCode
#ToString
public class Song {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "title")
private String title;
#JoinColumn(name = "album")
#ManyToOne
#OnDelete(action = OnDeleteAction.CASCADE)
private Album album;
#Column(name = "artists")
private String artists;
#Column(name = "track")
private int track;
#Column(name = "track_length")
private Time length;
}
DTO
package com.techm.react.Wasteland.dto;
import com.techm.react.Wasteland.models.Album;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.sql.Time;
#Data
#NoArgsConstructor
#AllArgsConstructor
public class SongDTO {
private String title;
private AlbumDTO album;
private String artists;
private int track;
private Time length;
}
Repo
package com.techm.react.Wasteland.repository;
import com.techm.react.Wasteland.models.Song;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
public interface SongRepo extends JpaRepository<Song, Integer> {
public abstract List<Song> findAll();
public abstract List<Song> findByArtist();
public abstract Song findByTrack();
}
Service
package com.techm.react.Wasteland.service;
import com.techm.react.Wasteland.dto.SongDTO;
import com.techm.react.Wasteland.models.Song;
import com.techm.react.Wasteland.repository.SongRepo;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
#Service #Configurable
public class SongService implements SongServiceInterface{
#Autowired
SongRepo songRepo;
#Autowired
private ModelMapper modelMapper;
public List<SongDTO> findAllSongs() {
List<SongDTO> songDTOS = new ArrayList<>();
List<Song> songs = songRepo.findAll();
for(Song s: songs) {
songDTOS.add(modelMapper.map(s, SongDTO.class));
}
return songDTOS;
}
public SongDTO getSongByTitle(String title) throws NoSuchFieldException {
SongDTO song = new SongDTO();
if(title == song.getTitle()){
return song;
}
else throw new NoSuchFieldException("The song by that title does not exist");
}
public SongDTO findByTrack(int track) {
SongDTO song = new SongDTO();
if(song.getTrack() == track) {
return song;
}
return null;
}
}
Main
package com.techm.react.Wasteland;
import com.techm.react.Wasteland.controller.AlbumController;
import com.techm.react.Wasteland.controller.SongController;
import com.techm.react.Wasteland.service.SongService;
import org.modelmapper.ModelMapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#SpringBootApplication
#ComponentScan({"com.server", "com.server.config"})
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class WastelandApplication {
#Bean
public ModelMapper modelMapper() {
ModelMapper modelMapper = new ModelMapper();
return modelMapper;
}
public static void main(String[] args) {
SpringApplication.run(WastelandApplication.class, args);
SongService songService = new SongService();
System.out.println(songService.findAllSongs());
}
}
When you use new to create an object, it's outside of Spring's context. It won't have the autowired dependencies. Also, you won't be able to use autowired beans in your main (nor should you) as it's a static method. If you want to do this in your app startup, autowire the bean and put the logic in a PostConstruct method.
Here is an example:
public class WastelandApplication {
#Autowired
private SongService songService;
#Bean
public ModelMapper modelMapper() {
ModelMapper modelMapper = new ModelMapper();
return modelMapper;
}
public static void main(String[] args) {
SpringApplication.run(WastelandApplication.class, args);
}
#PostConstruct
public void doSomethingIProbablyShouldNotBeDoing() {
System.out.println(songService.findAllSongs());
}
}
I'm trying to get a CrudRepository to work:
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.codynx.itemizer.model.Task;
#Repository
public interface TaskMysqlRepository extends CrudRepository<Task, Integer> {
}
used here:
import java.sql.ResultSet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.codynx.itemizer.model.Task;
import com.codynx.itemizer.repository.TaskMysqlRepository;
#Service
public class TaskMysqlService {
public ResultSet resultSet = null;
private Iterable<Task> tasks;
#Autowired
private TaskMysqlRepository taskMysqlRepository;
public TaskMysqlService() {
}
public Iterable<Task> getTasks(){
return taskMysqlRepository.findAll();
}
}
But I get the error message:
required a bean of type 'com.codynx.itemizer.repository.TaskMysqlRepository' that could not be found
The repo is there and has the correct annotation. What am I doing wrong?
Here is also the Task Type:
[...]
#AllArgsConstructor
#NoArgsConstructor
#Data
#Builder
public class Task {
#JsonProperty(access = Access.READ_ONLY)
#Id
private String id;
#Version
private Long version;
private LocalDateTime start;
private LocalDateTime due;
private boolean done;
private String name;
private Integer parent_id;
}
Is it because of some type missmatch? I mean the repo is there...
May be in your case due to package structure, autoconfiguration is not happening.
Try adding #EnableJpaRepositories and #EntityScan and mention the packages.
#SpringBootApplication
#EntityScan(basePackages = {"com.entities.package"}) //your entities package goes here
#EnableJpaRepositories(basePackages = {"com.repositories.package"})//your repository package goes here
public class SpringBootDataJpaApplication
{
public static void main(String[] args)
{
SpringApplication.run(SpringBootDataJpaApplication.class, args);
}
}
I have a class with lombok #RequiredArgsConstructor:
#RequiredArgsConstructor
#Service
public class Test{
private final String str;
private String str5;
// more code
}
In non-spring boot we provide in xml as:
<bean id="Test" class="com.abc.Test">
<constructor-arg index="0" value="${xyz}"/>
</bean>
how to achieve same from spring boot may be via application.properties but how to inject
The #Service annotation needs to be removed and the bean must be created in a #Configuration class with a #Bean annotated method returning that class type.
//Test.java
package com.abc;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
#RequiredArgsConstructor
#ToString
public class Test {
private final String str;
private String str5;
}
//DemoApplication.java
package com.abc;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
Test buildTest(#Value("${xyz}") String value) {
return new Test(value);
}
}
note: #SpringBootApplication implies #Configuration
//DemoApplicationTests.java
package com.abc;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
#SpringBootTest
class DemoApplicationTests {
#Autowired
com.abc.Test test;
#Test
void contextLoads() {
System.out.println(test);
}
}
#application.properties
xyz=print me
Result:
Test(str=print me, str5=null)
In this case I think you are better off either annotating the field :
#Service
public class Test{
#Value("${xyz}")
private String str;
private String str5;
// more code
}
or defining explicitly the constructor with an annotated parameter:
#Service
public class Test{
private final String str;
private String str5;
public Test(#Value("${xyz}") String str) {
this.str = str;
}
// more code
}
And if you have other final fields you can combine lombok constructor generation with field annotation like this as noted in Best practice for #Value fields, Lombok, and Constructor Injection?
#RequiredArgsConstructor
#Service
public class Test{
#Value("${xyz}")
private String str;
private final String str5;
// more code
}
I have abstract user service where I autowired two beans: Repository and AbstractMapper, but when I run test for that class, all faild with NullPointerException. When I run, for example, save test for that service in dubug, it show me that both beans are null. Anybody had this problem? This is my code:
AbstractService
package com.example.shop.service;
import com.example.shop.dto.AbstractMapper;
import com.example.shop.entity.Identifable;
import com.example.shop.repository.IRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
#Service`enter code here`
public abstract class AbstractService<E extends Identifable, D> implements IService<E, D> {
private IRepository<E> repository;
private AbstractMapper<E, D> abstractMapper;
#Autowired
public AbstractService(IRepository<E> repository, AbstractMapper<E, D> abstractMapper) {
this.repository = repository;
this.abstractMapper = abstractMapper;
}
#Override
public D get(Long id) {
E e = repository.get(id);
return abstractMapper.entityToDto(e);
}
#Override
public List<D> getAll() {
List<E> all = repository.getAll();
List<D> allDtos = all.stream()
.map(e -> abstractMapper.entityToDto(e))
.collect(Collectors.toList());
return allDtos;
}
#Override
public E save(D d) {
E e = abstractMapper.dtoToEntity(d);
return repository.save(e);
}
#Override
public E update(D d) {
E e = abstractMapper.dtoToEntity(d);
return repository.update(e);
}
#Override
public E delete(D d) {
E e = abstractMapper.dtoToEntity(d);
return repository.delete(e);
}
#Override
public void deleteAll() {
repository.deleteAll();
}
}
UserServiceImpl
package com.example.shop.service;
import com.example.shop.dto.UserDto;
import com.example.shop.dto.UserMapper;
import com.example.shop.entity.User;
import com.example.shop.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class UserServiceImpl extends AbstractService<User, UserDto> implements UserService {
private UserRepository repository;
private UserMapper userMapper;
#Autowired
public UserServiceImpl(UserRepository repository, UserMapper userMapper) {
super(repository, userMapper);
}
}
Abstract Mapper
package com.example.shop.dto;
import org.springframework.stereotype.Component;
#Component
public interface AbstractMapper<E, D> {
E dtoToEntity(D d);
D entityToDto(E e);
}
User Mapper:
package com.example.shop.dto;
import com.example.shop.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class UserMapper implements AbstractMapper<User, UserDto> {
private AccountMapper accountMapper;
#Autowired
public UserMapper(AccountMapper accountMapper) {
this.accountMapper = accountMapper;
}
#Override
public User dtoToEntity(UserDto dto) {
if (dto == null) {
return null;
}
User user = new User();
user.setId(dto.getId());
user.setEmail(dto.getEmail());
user.setPassword(dto.getPassword());
user.setLogin(dto.getLogin());
user.setAccount(accountMapper.dtoToEntity(dto.getAccountDto()));
return user;
}
#Override
public UserDto entityToDto(User user) {
if (user == null) {
return null;
}
UserDto dto = new UserDto();
dto.setEmail(user.getEmail());
dto.setLogin(user.getLogin());
dto.setPassword(user.getPassword());
dto.setId(user.getId());
dto.setAccountDto(accountMapper.entityToDto(user.getAccount()));
return dto;
}
}
Class with #SpringBootApplication
package com.example.shop;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class ShopApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(ShopApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
System.out.println("Test");
}
}
And my tests for Service:
package com.example.shop.service;
import com.example.shop.dto.UserDto;
import com.example.shop.entity.User;
import com.example.shop.repository.IRepository;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.mockito.Mockito.*;
#RunWith(SpringRunner.class)
#SpringBootTest
public class UserServiceImplTest {
private static final Long VALID_ID = 1L;
#Mock
private IRepository<User> repository;
#InjectMocks
private UserServiceImpl service;
#After
public void tearDown() {
repository.deleteAll();
}
#Test
public void get() {
service.get(VALID_ID);
verify(repository, times(1)).get(anyLong());
}
#Test
public void save() {
UserDto dto = createUser();
service.save(dto);
verify(repository, times(1)).save(any());
}
#Test
public void getAll() {
service.getAll();
verify(repository, times(1)).getAll();
}
#Test
public void update() {
UserDto dto = createUser();
service.update(dto);
verify(repository, times(1)).update(any());
}
#Test
public void delete() {
UserDto dto = createUser();
service.delete(dto);
verify(repository, times(1)).delete(any());
}
#Test
public void deleteAll() {
service.deleteAll();
verify(repository, times(1)).deleteAll();
}
private UserDto createUser() {
return new UserDto();
}
}
There are several problems with this code. First of all you do not need to annotate the abstract classes with service or component. Abstract classes cannot be instantiated, therefore there is no bean.
Second: autowire of classes having generics wont work. As soon as you have several bean, it wont be unique anymore.
Checkout if your classes get instantiated. Maybe you need to add #componentscan.
Your test is located under: com.example.shop.service and therefore it only scans the beans under this package. You should either move your test or add the beans by using the componentscan annotation
I'm trying to configure mongodb auditing in my spring boot app, and I having this error when trying to persist my domain class:
java.lang.IllegalArgumentException: Couldn't find PersistentEntity for type class com.example.hateoasapi.domain.Post!
Docs from here https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#auditing says that all this configs enough, but I don't know why it doesn't work in my project. Could someone help me?
My mongodb config class:
package com.example.hateoasapi.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
import org.springframework.data.mongodb.core.MongoTemplate;
import com.mongodb.MongoClient;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import java.util.Collection;
import java.util.Collections;
#Configuration
#EnableMongoAuditing
#EnableMongoRepositories(value = "com.example.hateoasapi.repository")
public class MongoConfig extends AbstractMongoConfiguration {
#Value("${spring.data.mongodb.database}")
private String databaseName;
#Value("${spring.data.mongodb.host}")
private String databaseHost;
#Value("${spring.data.mongodb.port}")
private Integer databasePort;
#Override
protected String getDatabaseName() {
return this.databaseName;
}
#Bean
#Override
public MongoClient mongoClient() {
return new MongoClient(databaseHost, databasePort);
}
#Bean
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoClient(), databaseName);
}
#Override
protected Collection<String> getMappingBasePackages() {
return Collections.singleton("com.example.hateoasapi.domain");
}
}
AuditorAware implementation:
package com.example.hateoasapi.config;
import com.example.hateoasapi.domain.User;
import org.springframework.data.domain.AuditorAware;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import java.util.Optional;
#Component
public class SecurityAuditor implements AuditorAware<User> {
#Override
public Optional<User> getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return null;
}
return Optional.of((User) authentication.getPrincipal());
}
}
And my domain class:
package com.example.hateoasapi.domain;
import javax.persistence.Id;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import org.joda.time.DateTime;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.hateoas.ResourceSupport;
import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;
import java.io.Serializable;
import java.util.List;
import com.example.hateoasapi.controller.*;
#Getter
#Setter
#ToString
#Document
public class Post extends ResourceSupport implements Serializable {
#Id
#Field(value = "_id")
private String objectId;
#DBRef
private List<Comment> comments;
#DBRef
private User author;
#NotBlank
private String body;
#NotBlank
private String title;
private String categoryId;
#NotEmpty(message = "Tags cannot be empty")
private List<PostTag> tags;
#CreatedDate
private DateTime createdDate;
#LastModifiedDate
private DateTime lastModifiedDate;
#CreatedBy
private User createdBy;
private Long views;
private List<PostRating> likes;
private List<PostRating> dislikes;
#JsonCreator
public Post() {}
public Post(String title, String body) {
this.body = body;
this.title = title;
}
public Post(User author, String body, String title, String categoryId, List<PostTag> tags) {
this.author = author;
this.body = body;
this.title = title;
this.categoryId = categoryId;
this.tags = tags;
}
public void addLinks() {
this.add(linkTo(methodOn(PostController.class).getAllPosts(null)).withSelfRel());
}
}
I solved this issue with the next configuration:
#Configuration
#EnableMongoRepositories(basePackages = "YOUR.PACKAGE")
#EnableMongoAuditing
public class MongoConfig extends AbstractMongoConfiguration {
#Value("${spring.data.mongodb.host}")
private String host;
#Value("${spring.data.mongodb.port}")
private Integer port;
#Value("${spring.data.mongodb.database}")
private String database;
#Override
public MongoClient mongoClient() {
return new MongoClient(host, port);
}
#Override
protected String getDatabaseName() {
return database;
}
#Bean
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory(), mappingMongoConverter());
}
#Bean
public MongoDbFactory mongoDbFactory() {
return new SimpleMongoDbFactory(mongoClient(), database);
}
}
just add the bean for MongoTemplate with the constructor of MongoTemplate(MongoDbFactory mongoDbFactory, #Nullable MongoConverter mongoConverter)
Quoting from JIRA ticket
You need to pipe the MappingMongoConverter that's available in the environment into MongoTemplate as well, i.e. use new MongoTemplate(dbFactory, converter). The constructor you use is for convenience, one-off usages. We usually recommend to use AbstractMongoConfiguration in case you'd like to customize anything MongoDB specific as this makes sure the components are wired together correctly.
More specifically, you need to inject pre-configured MappingMongoConverter or if you need to use your own converter, at least use pre-configured MongoMappingContext.
I had this problem also with spring boot 2.2
I had both #EnableMongoRepositories and #EnableMongoAuditing as configuration and i got the error Couldn't find PersistentEntity for type class
the problem in my case was the structure of the packages: Application class was a level lower than part of my model that used auditing.
I found on many forum posts that the 2 annotations are not compatible together in spring 2.2, but after restructuring the packages I was able to use both with success in spring boot 2.2
If you use the last version of Spring boot (2.0) and Spring Data, #EnableMongoAuditing
#EnableMongoRepositories are not compatible. It's the same with EnableReactiveMongoRepositories annotation.
If you want to enable mongo auditing, you need to remove your MongoConfig class, use config file to define your mongodb connection and everything will work.
If you use the last version of Spring boot (2.0) and Spring Data, #EnableMongoAuditing and #EnableMongoRepositories, try remove #EnableMongoRepositories. It should be working just this sample project - https://github.com/hantsy/spring-reactive-sample/tree/master/boot-data-mongo