spring jpa - At least one JPA metamodel must be present*

robyp7 picture robyp7 · Apr 6, 2017 · Viewed 33.7k times · Source

Anybody know why it doesn't work?

Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
06/04/2017 14:11:24.732 ERROR [main] - org.springframework.boot.SpringApplication: Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:742)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:866)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151)
    at com.cadit.web.WebApplicationAware.main(WebApplicationAware.java:19)
Caused by: java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
    at org.springframework.util.Assert.notEmpty(Assert.java:277)
    at org.springframework.data.jpa.mapping.JpaMetamodelMappingContext.<init>(JpaMetamodelMappingContext.java:52)
    at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.createInstance(JpaMetamodelMappingContextFactoryBean.java:71)
    at org.springframework.data.jpa.repository.config.JpaMetamodelMappingContextFactoryBean.createInstance(JpaMetamodelMappingContextFactoryBean.java:26)
    at org.springframework.beans.factory.config.AbstractFactoryBean.afterPropertiesSet(AbstractFactoryBean.java:134)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
    ... 16 common frames omitted

I defined entities in com.cadit.entities:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="TEST")
public class GenericBeans implements BeanType, IEntity<Long> {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "TEST_PAID")
    protected Long id;

    @Column(name = "SOCIETA")
    private String SocietaCod;
    @Column(name = "CONTO_INTERMEDIARIO")
    private String contoInt;
    @Column(name = "TIPO_OPERAZIONE")
    private String tipoOpe;


    public GenericBeans(String societaCod, String contoInt, String tipoOpe) {
        SocietaCod = societaCod;
        this.contoInt = contoInt;
        this.tipoOpe = tipoOpe;
    }


    public GenericBeans() {

    }




    public String getSocietaCod() {
        return SocietaCod;
    }


    public void setSocietaCod(String societaCod) {
        SocietaCod = societaCod;
    }


    public String getContoInt() {
        return contoInt;
    }


    public void setContoInt(String contoInt) {
        this.contoInt = contoInt;
    }


    public String getTipoOpe() {
        return tipoOpe;
    }


    public void setTipoOpe(String tipoOpe) {
        this.tipoOpe = tipoOpe;
    }


    @Override
    public String toString() {
        return "CSV [SocietaCod=" + SocietaCod + ", contoInt=" + contoInt + ", tipoOpe=" + tipoOpe + "]";
    }


    @Override
    public Long getId() {
        return this.id;
    }


    @Override
    public void setId(Long id) {
        this.id=id;     
    }

}

I definied my datasource entry definition for spring:

import org.apache.log4j.Logger;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan
@EntityScan("com.cadit.entities")
//@EnableJpaRepositories("com.cadit.entities")
@EnableTransactionManagement
@PropertySource("classpath:db-config.properties")
public class DbAutoConfiguration {


     static final Logger logger = Logger.getLogger(DbAutoConfiguration.class);

    public DbAutoConfiguration() {

    }


    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource(){
        //DataSource ds =new EmbeddedDatabaseBuilder().addScript("classpath:sql/schema.sql").addScript("classpath:testdb/data.sql").build();
        DataSourceBuilder ds =  DataSourceBuilder.create();
        logger.info("dataSource = " + ds);
        return ds.build();

    }
}

My db-config.properties is:

spring.jpa.hibernate.ddl-auto: validate
spring.jpa.hibernate.naming_strategy: org.hibernate.cfg.ImprovedNamingStrategy
#spring.jpa.database: SQL
spring.jpa.show-sql: true

spring.datasource.driverClassName=net.sourceforge.jtds.jdbc.Driver
spring.datasource.url=jdbc:jtds:sqlserver://localhost:1433;databaseName=example
spring.datasource.username=xxx
spring.datasource.password=xxx

IEntity is:

public interface IEntity <I extends Serializable> extends Serializable{

/**
  * Property rappresenta la primary key.
  */
  String P_ID = "id";

  /**
   * restituisce la primary key
   * @return
   */
  I getId();

  /**
   * imposta la primary key
   * @param id
   */
  void setId(I id);
}

I try to write CSV file to database using CrudRepository interface of spring:

import java.io.File;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.repository.CrudRepository;

import com.cadit.entities.GenericBeans;
import com.csvreader.CsvReader;

public class CsvReaders {

    static final Logger logger = Logger.getLogger(CsvReader.class);

    @Autowired
    public CrudRepository<GenericBeans,Long> _entitymanager;

    public List loadDataFromCsv(String fileName) {
        try {

            File file = new ClassPathResource(fileName).getFile();
            CsvReader csv = new CsvReader(file.getAbsoluteFile().getPath(),';');
            csv.readHeaders();
            List l = new LinkedList();
            GenericBeans b = new GenericBeans ();
            while (csv.readRecord())
            {
                b.setSocietaCod(csv.get(0));
                b.setContoInt(csv.get(1));
                b.setTipoOpe(csv.get(2));
                _entitymanager.save(b); //persist on db
                l.add(b);
                b = new GenericBeans();
            }
            b=null;
            return l;
        } catch (Exception e) {
            logger.error("Error occurred while loading object list from file " + fileName, e);
            return Collections.emptyList();
        }
    }


} 

I DO NOT use main class but a class which extend SpringBootServletInitializer because i want to run it on both standalone tomcat and Tomcat installation as WAR application

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;


@Configuration
@ComponentScan(basePackages={"com.cadit.entities","com.cadit.beans"})
@EnableAutoConfiguration
public class WebApplicationAware extends SpringBootServletInitializer {

    private static Class<WebApplicationAware> applicationClass = WebApplicationAware.class;

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

        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
            return application.sources(applicationClass);
        }




}

All properties file are in classpath resources because it's a maven project.

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>

    <groupId>org.springframework</groupId>
    <artifactId>xxxx</artifactId>
    <version>0.1.0</version>
    <packaging>war</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
    </parent>

    <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>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.jayway.jsonpath</groupId>
            <artifactId>json-path</artifactId>
            <scope>test</scope>
        </dependency>
       <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-jpa</artifactId>
        <version>1.11.1.RELEASE</version>
    </dependency>
    <dependency> 
        <groupId>javax.persistence</groupId> 
        <artifactId>persistence-api</artifactId> 
        <version>1.0.2</version> 
    </dependency> 


        <!-- altre dipendenze non spring -->
        <!-- https://mvnrepository.com/artifact/net.sourceforge.javacsv/javacsv -->
        <dependency>
            <groupId>net.sourceforge.javacsv</groupId>
            <artifactId>javacsv</artifactId>
            <version>2.0</version>
        </dependency>

        <!--  per jpa solo se si usa il Tomcat embedded -->
        <dependency>
            <groupId>net.sourceforge.jtds</groupId>
            <artifactId>jtds</artifactId>
            <version>1.3.1</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-dbcp2</artifactId>
            <version>2.1.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
        <!--  end -->


         <!-- dipendenze logback -->
      <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.5</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.5</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.7</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.1.7</version>
        </dependency>

        <!-- fine dip logback -->

    </dependencies>

    <properties>
     <start-class>hello.WebApplicationAware</start-class>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>


    <build>

   <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

    <repositories>
        <repository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>
</project>

What's the problem, why doesn't it find JPA entities when I run WebApplicationAware class?

Answer

Spring does not find any JPA Entities, so no JPA Meta Model is created, that is why you face the exception.

The cause of this problem may be a wrong persistence-api version on your class path.

You are using

<dependency> 
    <groupId>javax.persistence</groupId> 
    <artifactId>persistence-api</artifactId> 
    <version>1.0.2</version> 
</dependency> 

but I am pretty shure your spring version uses persistence-api version 2.

Could it be, you are using @Entity annotation from version 1 ? At runtime spring uses version 2, and this is searching for Entites using @Entity from version 2 only !

Remove the dependencies

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
</dependency>    
<dependency>
   <groupId>org.springframework.data</groupId>
   <artifactId>spring-data-jpa</artifactId>
   <version>1.11.1.RELEASE</version>
</dependency>

Instead add

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

This will give you all JPA dependencies in the right version.