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

34,815

Solution 1

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.

Solution 2

I solved it by adding 2 annotations

@EnableAutoConfiguration
@EntityScan(basePackages = { "com.wt.rds" })

and my dependency was in gradle

compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.0.4.RELEASE'
Share:
34,815
robyp7
Author by

robyp7

Updated on October 20, 2020

Comments

  • robyp7
    robyp7 over 3 years

    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?