Spring Boot with Hibernate - Autowiring Repository from Another Project

1.9k views Asked by At

I'm attempting to set up two separate projects:

  1. myApplicationWorker: backend server logic
  2. myApplicationCommon: contains models and other logic that will be shared between myApplicationWorker and future projects

My build.gradle dependencies for myApplicationWorker look like this:

dependencies {
    compile('org.springframework.boot:spring-boot-starter')
    compile project(':common')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

and my build.gradle dependencies for myApplicationCommon look like this:

dependencies {
    compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compile('mysql:mysql-connector-java')
    compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.9.1'
    compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.9.1'
    compile('org.apache.commons:commons-text:1.1')
    testCompile("com.h2database:h2")
    testCompile('junit:junit:5.0.2')
    testCompile('org.mockito.mockito-core:2.12.0')
}

(Contains testing/mocking/logging dependencies as well as JPA/MySQL dependencies.)

Simple hibernate/datasource properties are defined in application.properties:

spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=url
spring.datasource.username=username
spring.datasource.password=password

I have an "Account" model defined:

package com.myapplication.common.models;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "accounts")
public class Account {
    @Id
    private long id;

    //Other variables, getters/setters omitted for length

and the Account model has an AccountRepository

package com.myapplication.common.models.repositories;

import com.myapplication.common.models.Account;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface AccountRepository extends CrudRepository<Account, Long> {
    Account findAccountById(long id);
}

My goal is for any of my projects to be able to rely on the common project to interact with a backend MySQL database, simply by taking a dependency on the common project. I would expect this to work in my worker package:

package com.myapplication.worker;

import com.myapplication.common.models.repositories.AccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@EnableJpaRepositories(basePackages = {"com.myapplication.common"})
@EntityScan(basePackages = {"com.myapplication.common"})
public class Application {
    @Autowired
    private AccountRepository accountRepository;

    public static void main(String[] args) {
        Application application = new Application();
        application.testPrintAccountId();
    }

    public void testPrintAccountId() {
        System.out.println(accountRepository.findAccountbyId(1).getAccountId());
    }        
}

This gives me a NPE when I try to interact with accountRepository in testPrintAccountId(), however, it (obviously) works when I manually initialize the context:

public static void main(String[] args) {
    ConfigurableApplicationContext context = SpringApplication.run(Application.class);
    AccountRepository accountRepository = context.getBean(AccountRepository .class);
    System.out.println(accountRepository .findById(1234).getAccountId());

    (Successfully returns result)
}

I'm clearly misunderstanding how Spring and Spring Boot are supposed work here. How can I achieve my goal of being able to Autowire my repositories in my common project into other projects?

2

There are 2 answers

0
Panup Pong On

If you want to autowired the dependencies in main class. Spring has not yet autowired the dependencies into the App object. If you want to call this method after Spring has finished creating and autowiring, then add a method with a @PostConstruct annotation to do this, for example:

@SpringBootApplication
public class Application {
    @Autowired
    private AccountRepository accountRepository;

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }


    @PostConstruct
    public void testPrintAccountId() {
        System.out.println(accountRepository.findAccountbyId(1).getAccountId());
    }
}

or you can using that in @Component class such as @Controller @Service

0
GMeister On

In case anyone comes here, as I did.

This is possible, so presumably the OP had some other NPE issue. Here's what works for me in the non-core module/application:

@SpringBootApplication
@EnableJpaRepositories("com.package.domain.respositories")
@ComponentScan("com.package.domain")
public class ClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(ClientApplication.class, args);
    }

}

Tests run fine, and the ClientApp loads the repository from the Domain package without issue.