I'm trying to save an object with Spring Data JPA. Unfortunately I always get a NullPointerException. But I don't understand why. I followed this tutorial: https://www.callicoder.com/spring-boot-jpa-hibernate-postgresql-restful-crud-api-example/ I don't need a CRUD API so I left out the things that belong to it.

I get the Following Error:

java.lang.NullPointerException
    at com.niclas.elitedangerousapi.handler.SystemPopulatedHandler.insertIntoDB(SystemPopulatedHandler.java:39)
    at com.niclas.elitedangerousapi.Main.main(Main.java:19)
[main] ERROR c.n.e.h.SystemPopulatedHandler - null

UPDATE I want to fill my database, but then when I want. At the end it should be so that every night a file is downloaded and then stored in the Database. Later I want to make the data accessible via an API.I want to execute this method (systemPopulatedHandler.insertIntoDB()) at the start and every x hours.

SystemPopulated.class

@Data
@Entity
@Table(name = "systems_populated")
@JsonIgnoreProperties(ignoreUnknown = true)
public class SystemPopulated {

    @Id
    @Column(name = "id")
    private int id;

    @Column(name = "edsm_id")
    private long edsm_id;

    @Column(name = "name")
    private String name;

    @Column(name = "x")
    private double x;

    @Column(name = "y")
    private double y;

    @Column(name = "z")
    private double z;

    @Column(name = "population")
    private long population;

    @Column(name = "is_populated")
    private boolean is_populated;

    @Column(name = "government_id")
    private long government_id;

    @Column(name = "government")
    private String government;

    @Column(name = "allegiance_id")
    private int allegiance_id;

    @Column(name = "allegiance")
    private String allegiance;

    @Column(name = "security_id")
    private int security_id;

    @Column(name = "security")
    private String security;

    @Column(name = "primary_economy_id")
    private int primary_economy_id;

    @Column(name = "primary_economy")
    private String primary_economy;

    @Column(name = "power")
    private String power;

    @Column(name = "power_state")
    private String power_state;

    @Column(name = "power_state_id")
    private int power_state_id;

    @Column(name = "needs_permit")
    private boolean needs_permit;

    @Column(name = "updated_at")
    private long updated_at;

    @Column(name = "controlling_minor_faction_id")
    private int controlling_minor_faction_id;

    @Column(name = "controlling_minor_faction")
    private String controlling_minor_faction;

    @Column(name = "reserve_type_id")
    private int reserve_type_id;

    @Column(name = "reserve_type")
    private String reserve_type;
}

My SystemPopulatedReposetory.class

@Repository
public interface SystemPopulatedRepository extends JpaRepository<SystemPopulated, Integer> {
}

My Class where i want to InsertIntoDB SystemPopulatedHandler.class

@Slf4j
public class SystemPopulatedHandler {

    @Autowired
    private SystemPopulatedRepository systemPopulatedRepository;

    public void insertIntoDB() {

        BufferedReader reader;
        try {
            reader = new BufferedReader( new FileReader(DOWNLOAD_SAVE_PATH + FILE_NAME_SYSTEMS_POPULATED) );
            String line = reader.readLine();
            while( line != null ){

                ObjectMapper mapper = new ObjectMapper();

                systemPopulatedRepository.save( mapper.readValue( line, SystemPopulated.class ) );

                line = reader.readLine();
            }
            reader.close();
        }
        catch( Exception e ) {
            e.printStackTrace();
            log.error( e.getLocalizedMessage() );
        }
    }

}

My Main.class

@SpringBootApplication
@EnableJpaRepositories
public class Main {
    public static void main( String[] args ) {
        SpringApplication.run( Main.class, args );

        SystemPopulatedHandler systemPopulatedHandler = new SystemPopulatedHandler();
        systemPopulatedHandler.insertIntoDB();
  }
}

4 Answers

1
BeWu On Best Solutions

The problem is that you create SystemPopulatedHandler yourself with

SystemPopulatedHandler systemPopulatedHandler = new SystemPopulatedHandler();

That way spring isn't injecting the repository into your class because that works only if spring creates the class.

But if you want to populate a database at startup (at least it seems that you try to do that) you should check out flyway (or 85.5 in this documentation: https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html)

If you can't use flyway for some reason you can add the following code to the main class:

@Bean
public SystemPopulatedHandler systemPopulatedHandler(SystemPopulatedRepository repository) {
    SystemPopulatedHandler systemPopulatedHandler = new SystemPopulatedHandler(repository);
    systemPopulatedHandler.insertIntoDB()
    return systemPopulatedHandler;
}

Afterwards add the constructor to the SystemPopulatedHandler class:

public SystemPopulatedHandler(SystemPopulatedRepository systemPopulatedRepository) {
    this.systemPopulatedRepository = systemPopulatedRepository;
}

and remove the @Autowired annotation.

update

You also need to add the missing annotations as mentioned by this answer: https://stackoverflow.com/a/55767393/2248239

update 2

If you want to do that action periodically you can use scheduling (like in this guide https://spring.io/guides/gs/scheduling-tasks/) Actually that's pretty easy:

Don't do the changes I mentioned above except adding the missing annotations and just do the following:

Add @Component to SystemPopulatedHandler

Add @Scheduled to insertIntoDB() in SystemPopulatedHandler

And add @EnableScheduling to the main class

For @Scheduled just read the guide it describes what you can do with the annotation.

0
DEBENDRA DHINDA On

This is due to the repository you have auto wired is not wiring the bean.

Please annonate your repository with @Repository And in main class specify @EnableJpaRepository.

See the spring data JPA docs for more details

0
Sherif Ibrahim On

The problem is that you are instantiating the SystemPopulatedHandler bean without using ApplicationContext or BeanFactory, So it's not maintained by IOC container.

To use DI in a SpringBoot application all you need is to just auto wire SystemPopulatedHandler in your controller or service and then you can call insertIntoDB() method.

Since you are using spring boot and example is so simple you needn't make separate Configuration for beans.

@Controller
public class SystemPopulatedController {

    @Autowired
    private SystemPopulatedHandler systemPopulatedHandler;

    @RequestMapping("/")
    public void insertIntoDB() {

      systemPopulatedHandler.insertIntoDB();
    }

}
0
StitZle On

Many thanks to all of you. I have solved the problem as follows:

SystemPopulatedReposetory.class

@Repository
public interface SystemPopulatedRepository extends JpaRepository<SystemPopulated, Integer> {
}
´´´

SystemPopulatedHandler.class

@Slf4j
@Component
public class SystemPopulatedHandler {

    @Autowired
    private SystemPopulatedRepository systemPopulatedRepository;

    @PostConstruct
    @Scheduled(cron = "0 0 0 * * *")
    public void insertIntoDB() {

        BufferedReader reader;
        try {
            reader = new BufferedReader( new FileReader(DOWNLOAD_SAVE_PATH + FILE_NAME_SYSTEMS_POPULATED) );
            String line = reader.readLine();
            while( line != null ){

                ObjectMapper mapper = new ObjectMapper();

                systemPopulatedRepository.save( mapper.readValue( line, SystemPopulated.class ) );

                line = reader.readLine();
            }
            reader.close();
        }
        catch( Exception e ) {
            e.printStackTrace();
            log.error( e.getLocalizedMessage() );
        }
    }
}

´´´

Main.class

@SpringBootApplication
@EnableJpaRepositories
@EnableScheduling
public class Main {
    public static void main( String[] args ) {
        SpringApplication.run( Main.class, args );

        FileHandler fileHandler = new FileHandler();

    }
}