Vaadin 13.0.3 and Spring JpaRepository

Hello, i don’t have a lot of experience using Vaadin, and recently I’ve been trying to use JpaRepository in my Vaadin project.
I’m having a problem when I try to inject the class UsuarioDao, my JpaRepository using @Autowired in one of my views, called CadastroUsuario.

The error that I’m getting:

There was an exception while trying to navigate to ‘cadastroUsuario’ with the exception message ‘Error creating bean with name ‘br.edu.unoesc.views.CadastroUsuario’: Unsatisfied dependency expressed through field ‘usuarioDao’; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ‘br.edu.unoesc.dao.UsuarioDao’ available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}’

With this exception:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘br.edu.unoesc.views.CadastroUsuario’: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ‘br.edu.unoesc.dao.UsuarioDao’ available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ‘br.edu.unoesc.dao.UsuarioDao’ available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

Code:

Interface UsuarioDao


@Repository
public interface UsuarioDao extends JpaRepository<Usuario, Long>{
	
	void findByNome(String nome);
	
}

Class CadastroUsuario:

@PageTitle("Gestão de Safra")
@Route("cadastroUsuario")
@HtmlImport("frontend://styles/tema.html")
@ComponentScan(basePackages = {"br.edu.unoesc.dao"} )
public class CadastroUsuario extends Div{

	private static final long serialVersionUID = -5497533232441585612L;

	private FormLayout form = new FormLayout();
	private TextField nome = new TextField();
	private TextField sobrenome = new TextField();
	private EmailField email = new EmailField();
	private PasswordField senha = new PasswordField();
	private PasswordField confirmacaoSenha = new PasswordField();
	private DatePicker dataNascimento = new DatePicker();
	private Button salvar = new Button("Salvar");
	private Button limpar = new Button("Limpar todos os campos");
	
	Binder<Usuario> binder = new Binder<>(Usuario.class);
	
	private Usuario usuario;
	
	@Autowired
	private UsuarioDao usuarioDao;
	
	public CadastroUsuario(){
		
		add(new H2("Cadastro de Usuário"));
		
		nome.setPlaceholder("Primeiro nome");
		nome.setAutofocus(true);
		nome.setValueChangeMode(ValueChangeMode.EAGER);
		sobrenome.setPlaceholder("Segundo nome");
		sobrenome.setValueChangeMode(ValueChangeMode.EAGER);
		email.setPlaceholder("nome@exemplo.com");
		email.setValueChangeMode(ValueChangeMode.EAGER);
		senha.setPlaceholder("Senha de acesso");
		senha.setValueChangeMode(ValueChangeMode.EAGER);
		confirmacaoSenha.setPlaceholder("Confirmação da senha");
		confirmacaoSenha.setValueChangeMode(ValueChangeMode.EAGER);
		dataNascimento.setPlaceholder("Nascimento");
		dataNascimento.setLocale(new Locale("br"));
		dataNascimento.setI18n(new DataPickerPt().dataPt());
		        
		salvar.setThemeName("primary");
		limpar.setThemeName("secondary");
		
		// adicionando nomes nos campos do formulario
		form.addFormItem(nome, "Nome: ");
		form.addFormItem(sobrenome, "Sobrenome: ");		
		form.addFormItem(email, "E-mail: ");
		form.addFormItem(dataNascimento, "Data de Nascimento");
		form.addFormItem(senha, "Senha: ");
		form.addFormItem(confirmacaoSenha, "Confirme sua senha: ");
		
		// bind usando convensao de nome
//		binder.bindInstanceFields(this);
		
		binder.forField(nome).bind(Usuario::getNome, Usuario::setNome);
		binder.forField(sobrenome).bind(Usuario::getSobrenome, Usuario::setSobrenome);
		binder.forField(email).bind(Usuario::getEmail, Usuario::setEmail);
		binder.forField(senha).bind(Usuario::getSenha, Usuario::setSenha);
		
		binder.forField(dataNascimento).withConverter(new SqlDateToLocalDateConverter())
	       .bind(Usuario::getDataNascimento, Usuario::setDataNascimento);
		
		// campos required
		nome.setRequiredIndicatorVisible(true);
		sobrenome.setRequiredIndicatorVisible(true);
		email.setRequiredIndicatorVisible(true);
		senha.setRequiredIndicatorVisible(true);
		confirmacaoSenha.setRequiredIndicatorVisible(true);
		dataNascimento.setRequiredIndicatorVisible(true);
		
		// Button bar
		HorizontalLayout actions = new HorizontalLayout();
		actions.setSpacing(true);
		actions.add(salvar, limpar);
		
		salvar.addClickListener(e ->{
			if(senha.isEmpty() || senha.getValue().contentEquals(confirmacaoSenha.getValue())) {
//				Usuario usuario = new Usuario();
//				usuario.setNome(nome.getValue());
//				usuario.setSobrenome(sobrenome.getValue());
//				usuario.setEmail(email.getValue());
//				usuario.setSenha(senha.getValue());
//				usuario.setDataNascimento(Date.from(dataNascimento.getValue().atStartOfDay(ZoneId.systemDefault()).toInstant()));
//				System.out.println(usuario.toString());
				
				binder.readBean(usuario);
				usuarioDao.save(usuario);
				
				salvar.getUI().ifPresent(ui -> ui.navigate(""));
				
			}else {
				Dialog dialog = new DialogMensagem().erroForm();
				dialog.open();
			}
		});
		
		limpar.addClickListener(event -> {
			nome.clear();
			sobrenome.clear();
			email.clear();
			senha.clear();
			confirmacaoSenha.clear();
			dataNascimento.clear();
		});
		
		add(form, actions);
		
		
	}	
	
}


Class PersistenceConfig enabling JpaRepository:


@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "br.edu.unoesc")
public class PersistenceConfig {

}

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.4.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>br.edu.unoesc</groupId>
	<artifactId>VaadinSpring</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>VaadinSpring</name>
	<description>Demo project for Spring Boot and Vaadin</description>

	<properties>
		<java.version>1.8</java.version>
		<vaadin.version>13.0.3</vaadin.version>
	</properties>

	<dependencies>
	
	<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>5.1.6.RELEASE</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>5.1.6.RELEASE</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa -->
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-jpa</artifactId>
			<version>2.1.6.RELEASE</version>
		</dependency>
	

	

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>


		<dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-spring-boot-starter</artifactId>
        </dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		
		
		<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
		<dependency>
		    <groupId>mysql</groupId>
		    <artifactId>mysql-connector-java</artifactId>
		    <version>8.0.16</version>
		</dependency>


		<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>5.4.2.Final</version>
		</dependency>
		
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
		
		<!-- https://mvnrepository.com/artifact/org.webjars.bowergithub.vaadin/vaadin-lumo-styles -->
		<dependency>
		    <groupId>org.webjars.bowergithub.vaadin</groupId>
		    <artifactId>vaadin-lumo-styles</artifactId>
		</dependency>
		


		<!-- https://mvnrepository.com/artifact/commons-configuration/commons-configuration -->
		<dependency>
		    <groupId>commons-configuration</groupId>
		    <artifactId>commons-configuration</artifactId>
		    <version>1.10</version>
		</dependency>


		
		
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>com.vaadin</groupId>
				<artifactId>vaadin-bom</artifactId>
				<version>13.0.4</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Git repository: https://github.com/TheylorMarmitt/VaadinExemplo

You might need to tell Spring Boot which Java packages to scan for repositories and entities, e.g.

@SpringBootApplication(scanBasePackageClasses = { SomeClass.class })
@EnableJpaRepositories(basePackageClasses = { UsuarioDao.class })
@EntityScan(basePackageClasses = { Usuario.class })

@ComponentScan(basePackages = {“br.edu.unoesc.dao”} ) on the route is useless. This is a component not a configuration.

Can you show the main class where you have put the @SpringBootApplication annotation and how does your package structure?

If you put this class in a package above all other components you don’t have to configure anything (that’s Spring Boot best practice)

The @ComponentScan(basePackages = {"br.edu.unoesc.dao"} ) annotation should be in the spring configuration (a @Configuration class), not in the view class. Because your Spring is now missing a component scan annotation, your repository will not be found by spring and cannot inject it into your view.

I made the following changes and I’m still getting the same error

Class CadastroUsuario removed @ComponentScan(basePackages = {“br.edu.unoesc.dao”} )

@PageTitle("Gestão de Safra")
@Route("cadastroUsuario")
@HtmlImport("frontend://styles/tema.html")
public class CadastroUsuario extends Div{

Class PersistenceConfig add some annotations

@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = {"br.edu.unoesc.dao"} )
@SpringBootApplication(scanBasePackageClasses = { VaadinSpringApplication.class })
@EnableJpaRepositories(basePackageClasses = { UsuarioDao.class })
@EntityScan(basePackageClasses = { Usuario.class })
public class PersistenceConfig {

}

And this is my main class with @SpringBootApplication

@Configuration
@EnableVaadin
@SpringBootApplication
@PropertySource("classpath:application.properties")
public class VaadinSpringApplication extends SpringBootServletInitializer {

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

	@Override
	public void onStartup(ServletContext servletContext)
			throws ServletException {
		AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();

		servletContext.addListener(new ContextLoaderListener(context));

		ServletRegistration.Dynamic registration = servletContext
				.addServlet("dispatcher", new SpringServlet(context, true));
		registration.setLoadOnStartup(1);
		registration.addMapping("/*");
	}
}

Since the implementor of SpringBootServletInitializer is the class VaadinSpringApplication, this will be your starting point. This class needs to know about other configurations, and not the other way around. In your PersiscenteConfig class you scan for the VaadinSpringApplication class, but this should be the other way around.
To fix this, change the @SpringBootApplication annotation in your VaadinSpringApplication class to scan for all your other configuration classes:

// assuming the PersistenceConfig class is in the package 'br.edu.unoesc.configuration' 
// this will also pick up any other @Configuration class (any @component in fact) in that package
@SpringBootApplication(scanBasePackages = {"br.edu.unoesc.configuration"})
public class VaadinSpringApplication extends SpringBootServletInitializer {

// or
@SpringBootApplication(scanBasePackageClasses = {PersistenceConfig .class})
public class VaadinSpringApplication extends SpringBootServletInitializer {

while removing the @SpringBootApplication annotation in the PersistenceConfig class.

I’ve made the suggested changes, it works.

Thanks Kaspar Scherrer.