At least one JPA metamodel must be present - java

I am getting this exception while testing my Controller class
Caused by: java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
at org.springframework.util.Assert.notEmpty(Assert.java:450)
at org.springframework.data.jpa.mapping.JpaMetamodelMappingContext.<init>(JpaMetamodelMappingContext.java:54)
at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.createInstance(JpaMetamodelMappingContextFactoryBean.java:88)
at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.createInstance(JpaMetamodelMappingContextFactoryBean.java:43)
at org.springframework.beans.factory.config.AbstractFactoryBean.afterPropertiesSet(AbstractFactoryBean.java:141)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1761)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1698)
My controller test class looks like this
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = {SensorController.class}, secure = false)
public class SensorControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private SensorService sensorService;
.....
}
My Bootstrap class
#SpringBootApplication(scanBasePackages = "com.javadroider")
#RestController
#EntityScan("com.javadroider.notifier.commons.model")
#EnableJpaRepositories(basePackages = "com.javadroider.notifier")
public class NotifierApplication {
#GetMapping("/")
public String home(){
return "Welcome to Notifier";
}
public static void main(String[] args) {
SpringApplication.run(NotifierApplication.class, args);
}
}
My dependencies look like this
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
When I remove #EnableJpaRepositories from my Bootstrap class then controller test will be successful. But application will not start. It will fail with NoSuchBeanDefinitionException.
I am not sure if it is something related to the way I have configured my application. All my repository & model classes are in commons module and I am referring to them in my main aplpication.
My query is similar to https://github.com/spring-projects/spring-boot/issues/6844
IllegalArgumentException: At least one JPA metamodel must be present didn't solve my problem

The #WebMvcTest annotation does not auto configure any repositories or JPA layer beans for you because it is primarily focused on testing just the Controller level.
Your main class NotifierApplication is trying to do too much. See the single responsibility principle for good programming practices: https://en.wikipedia.org/wiki/Single_responsibility_principle
The main class should be used just to define your application it should not itself be a #RestController. Create a separate class, NotifierController, for example and make this your #RestController with your endpoints defined there:
#RestController
public class NotifierController {
#GetMapping("/")
public String home(){
return "Welcome to Notifier";
}
}
and keep this separate from your main class:
#SpringBootApplication(scanBasePackages = "com.javadroider")
#EntityScan("com.javadroider.notifier.commons.model")
#EnableJpaRepositories(basePackages = "com.javadroider.notifier")
public class NotifierApplication {
public static void main(String[] args) {
SpringApplication.run(NotifierApplication.class, args);
}
}
The reason it was failing the way you had it is that your WebMvcTest was trying to scan for JPA repositories and entities due to the annotations on the main class but this is not configured for WebMvcTests.

Related

Can't deploy war to Tomcat

I have spring boot application and it works when i run it with spring boot,but when I build war and deploy it to tomcat,i get 404 status
I added this lines from spring docs to maven:
<properties>
<start-class>com.example.deploytest.DeploytestApplication</start-class>
</properties>
<packaging>war</packaging>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
And here is my main class:
#SpringBootApplication
public class DeploytestApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(DeploytestApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(DeploytestApplication.class);
}
}
And simple controller:
#RestController
public class MainController {
#GetMapping
public String hello() {
return "hello world";
}
}
My tomcat version is 10.0.27
What is wrong here?
The pom.xml should look similar to this:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
If missing you can also try to add the SpringBootServletInitializer interface in you main class:
#SpringBootApplication
public class SpringBootTomcatApplication extends SpringBootServletInitializer {
}
try to give an url to your GetMapping
#GetMapping("/")
servlet api version mismatch, try to use the same version of tomcat as embedded.

Spring Java - Parameter 0 of constructor

I'm trying to write a simple CRUD program and I get this error. The program is based after codecademy project. Not sure why I doesn't work.
If I comment out the constructor the error disappears.I don't have anything in my properties.
Can someone give me a hand?
Description:
Parameter 0 of constructor in com.example.FitApp3.controller.FoodController required a bean of type 'com.example.FitApp3.repository.FoodRepository' that could not be found.
Action:
Consider defining a bean of type 'com.example.FitApp3.repository.FoodRepository' in your configuration.
Process finished with exit code 1
This is my code:
Entity/Food.java
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class Food extends com.example.FitApp3.model.Entity {
private String foodName;
private int foodKcal;
private int foodProtein;
private int foodCarb;
private int foodFat;
public String getFoodName() {
return foodName;
}
public void setFoodName(String foodName) {
this.foodName = foodName;
}
public int getFoodKcal() {
return foodKcal;
}
public void setFoodKcal(int foodKcal) {
this.foodKcal = foodKcal;
}
public int getFoodProtein() {
return foodProtein;
}
public void setFoodProtein(int foodProtein) {
this.foodProtein = foodProtein;
}
public int getFoodCarb() {
return foodCarb;
}
public void setFoodCarb(int foodCarb) {
this.foodCarb = foodCarb;
}
public int getFoodFat() {
return foodFat;
}
public void setFoodFat(int foodFat) {
this.foodFat = foodFat;
}
}
Repository/FoodRepository.java
public interface FoodRepository extends CrudRepository<Food, Integer> {}
Controller/FoodController.java
#RestController
public class FoodController {
private FoodRepository foodRepository;
public FoodController(FoodRepository foodRepository) {
this.foodRepository = foodRepository;
}
}
Mainclass
#SpringBootApplication
public class FitApp3Application {
public static void main(String[] args) {
SpringApplication.run(FitApp3Application.class, args);
System.out.println("hello world");
}
}
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 https://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.6.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>FitApp3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>FitApp3</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.5.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Can you add #Repository annotation to FoodRepository interface.
#Repository
public interface FoodRepository extends CrudRepository<Food, Integer> {}
You need to replace spring-boot-starter-jdbc with spring-boot-starter-data-jpa as follows:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
You can have a look at the following links the difference between them, but basically, JPA helps you deal with your Database data by mapping it directly to Java objects:
https://www.baeldung.com/jpa-vs-jdbc
JPA or JDBC, how are they different?
I think you are just a little bit confused with spring-jdbc, spring-data and spring persistence interfaces abstraction, let me help. TL;DR solution is at the bottom:
In the constructor of FoodController you are declaring FoodRepository as a parameter, so spring have to find the bean of this time at runtime in order to create bean of FoodController. You have declared FoodRepository and extend it from CrudRepository. I guess, that you have done it with the assumption, that spring will create an implementation of FoodRepository at runtime (because it extends CrudRepository). But, unfortunately, it is not the spring-core module, that will create bean of FoodRepsitory, nor it is spring-jdbc. This interfaces are the part of spring-data project. So, for creation of the bean of the type FoodRepository is responsible current spring-data project in your classpath (I mean, it could be spring-data-jdbc, spring-data-jpa or whatever). This interfaces (CrudRepository, Repository e.t.c.) are common for all of the spring data projects, so they are shipped in spring-data-commons jar, this is why you have them available in classpath, because you have included spring-data-commons explicitly:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.5.1</version>
</dependency>
Another thing is that this starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
does not do anything with spring-data project - it just brings spring-jdbc, Hikari connection pool, and some other spring-boot stuff. In other words, the problem is that you do not have any spring-data project in your classpath, thats why FoodRepository bean is not created.
Also note: #Entity does not make sense here, becuase you do not have any jpa persistence provider in classpath
Solution:
To solve the problem, I suggest you to
Remove spring-data-commons dependency from pom.xml
Include spring-boot-starter-data-jpa into your project (into pom.xml). It will bring the appropriate version of spring-data-commons dependency.
Hope it helped, have a nice day!
Add #Repository annotation to FoodRepository interface
and also #Autowired annotation to the FoodRepository in FoodController.

Spring-boot just doesn't seem to get the repository

I can't use my CrudRepository in my Service class. I can create the repository, but when I Autowire it to my Service class I get this error:
Parameter 0 of constructor in com.test.service.testService required a bean of type 'com.test.repository.TestRepository' that could not be found.
Action:
Consider defining a bean of type 'com.test.repository.TestRepository' in your configuration.
This seems to be a big issue for many people. I've tried all kinds of things like #ComponentScan, #EnableAutoConfiguration, #EnableJpaRepositories but none of that worked.
Main App:
#ComponentScan ({"com.test.repository", "com.test.controller","com.test.service","com.test.model"})
#EnableJpaRepositories
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class);
}
}
Service:
public testService(TestRepository testRepository) {
this.testRepository= testRepository;
}
Repository
package com.test.TestRepository;
import com.test.model.Item;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface TestRepository extends CrudRepository<Item, Long> {
}
POM.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
</dependencies>
I expect the repository to be Autowired and fully functional.
Try this:
#EnableJpaRepositories(basePackages = {"com.test"})
Try remove the #Repository annotation above your TestRepository interface.
"Spring #Repository annotation is used to indicate that the class provides the mechanism for storage, ... " see:https://www.journaldev.com/21460/spring-repository-annotation
Suggestion: Modify your TestRepository interface to extend the JpaRepository. It has more functionality.

#RepositoryRestController makes REST API not available

I have created simple Spring BOOT application with Spring Data Rest. It works fine with repositories. However, I need to add some extra functionality and for that purpose would like to use controller class annotated with #RepositoryRestController. The issue is when I use #RestController annotation GET method is available for calling but when I switch annotation to #RepositoryRestController browser says the method is not available.
POM file:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Controller class:
#RepositoryRestController
public class HelloContoller {
#RequestMapping("/test")
#ResponseBody
public String sayHello() {
return "Hi, there!";
}
#RequestMapping(value = "test", method = RequestMethod.POST)
public String postHello() {
return "POST!";
}
#RequestMapping(value = "/test", method = RequestMethod.PUT)
public String putHello() {
return "PUT!";
}
}
Configuration class:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
I am new to Spring Data Rest and would appreciate any help!
Thanks!
In case of overriding just some methods of the ones generated by spring-data-rest's RepositoryEntityController I also had problems that you ran into.
It seems that in such cases you cannot use the class level #RequestMapping annotation on a RepositoryRestController.
So remove #RequestMapping("/test") from the class and repeat the /test path on each method.
Finally, I have managed to find answer with help from Vladimir Tsukur. In order to use #RepositoryRestController properly you must create repository interface first and then use its URL as base path for #RequestMapping on each method of the controller.
Now everything works fine. Just be careful about the URLs :)

#Value not set via Java-configured test context

I've got a Maven project which uses Java-configured Spring (#Configuration etc.). Properties which are referenced by #Value are stored in different places, e.g. Tomcat's context.xml.
For testing I've created a .properties file to provide some values for the components and services. In my JUnit test (which uses a spring test context) this .properties file is added via #PropertySource. The problem is that the values will not be loaded from the file, instead the value identifier is set as value, e.g. ${someFlag:false} (so I get ClassCastExceptions for any other than String). Also the default value will not be set, so I think, the values won't be processed at all.
I'm sure Spring finds this file because when I change the value of #PropertySource I get some FileNotFoundException. Nevertheless I've tried different variants to point to this file an all have worked (tested by renaming which produced FileNotFoundException):
classpath:/test.properties (my preferred notation)
/test.properties
file:src/test/resources/test.properties
I'm also sure that Spring itself works, because when I remove the #Value, the class under test is injected via #Autowired in my test as expected.
Down below you'll find the problem scenario stripped down as much as possible. For versions and dependencies please see the pom.xml at the bottom.
MyService.java
package my.package.service;
// Imports
#Service
public class MyService {
#Value("${someFlag:false}")
private Boolean someFlag;
public boolean hasFlag() {
return BooleanUtils.isTrue(someFlag);
}
}
MyConfiguration.java
#Configuration
#ComponentScan(basePackages = {"my.package.service"})
public class MyConfiguration {
}
MyServiceComponentTest.java
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {MyTestConfiguration.class})
public class MyServiceComponentTest {
#Autowired
private MyService service;
#Test
public void hasFlagReturnsTrue() {
assertThat(service.hasFlag(), is(true));
}
}
MyTestConfiguration.java
#Configuration
#Import({MyConfiguration.class})
#PropertySource("classpath:/test.properties")
public class MyTestConfiguration {
}
src/test/resources/test.properties
someFlag=true
pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>3.2.3.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
The issue here is you need a PropertySourcesPlaceholderConfigurer also which is actually responsible for resolving the ${..} fields, just add another bean which creates this bean:
#Bean
public static PropertySourcesPlaceholderConfigurer propertiesResolver() {
return new PropertySourcesPlaceholderConfigurer();
}
With Spring 4, it's now possible to use TestPropertySource:
#TestPropertySource(value="classpath:/config/test.properties")
In order to load specific properties for a junit test
In addition to Biju Kunjummen answer:
If you use #ConfigurationProperties to inject properties into bean setters, then ConfigurationPropertiesBindingPostProcessor need to be created (instead of PropertySourcesPlaceholderConfigurer):
#Configuration
static class PropertyConfig {
#Bean
public static ConfigurationPropertiesBindingPostProcessor propertiesProcessor() {
return new ConfigurationPropertiesBindingPostProcessor();
}
}

Categories