spring jpa - 必须存在至少一个 JPA 元模型*
spring jpa - At least one JPA metamodel must be present*
有人知道为什么它不起作用吗?
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.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
我在 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;
}
}
我为 spring 定义了我的 datasource
条目定义:
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();
}
}
我的db-config.properties
是:
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
是:
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);
}
我尝试使用 spring 的 CrudRepository
接口将 CSV 文件写入数据库:
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();
}
}
}
我不使用 main
class 但 class 扩展 SpringBootServletInitializer
因为我想 运行 它在两个独立 tomcat 和 Tomcat 安装为 WAR 应用程序
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);
}
}
所有属性文件都在 classpath 资源中,因为它是一个 maven 项目。
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>
什么问题,为什么当我 运行 WebApplicationAware class 时它找不到 JPA 实体?
Spring 未找到任何 JPA 实体,因此未创建 JPA 元模型,这就是您遇到异常的原因。
此问题的原因可能是您的 class 路径上的错误持久性-api 版本。
您正在使用
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
但我很确定您的 spring 版本使用持久性-api 版本 2。
难道您使用的是版本 1 中的 @Entity 注释?
在运行时 spring 使用版本 2,这仅使用版本 2 中的@Entity 搜索实体!
删除依赖项
<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>
改为添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
这将为您提供正确版本的所有 JPA 依赖项。
我加了2个注解就解决了
@EnableAutoConfiguration
@EntityScan(basePackages = { "com.wt.rds" })
我的依赖项在 gradle
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.0.4.RELEASE'
不幸的是,大多数关于 JPA 集成测试的 spring 引导指南经常缺少一些配置。
所以这是一个希望对你有用的例子。
第 1 点。
我的本地环境当前设置为使用 spring 引导版本:
<version.spring.boot>1.5.9.RELEASE</version.spring.boot>
也就是说,我目前正在设置我的本地环境,以便能够 运行 针对多个数据库(例如 postgres、hsql、h2)进行集成测试。
因此,我首先在谷歌上搜索任何解决这个问题的随机教程。
下一个 link 就是这样一个例子:
https://www.baeldung.com/spring-testing-separate-data-source
上面的例子是一个很好的起点。它允许您获取一个有效的实体和一个有效的存储库。另一方面,spring 引导测试 class 本身还有很多不足之处。
通过上面的示例,您将立即为集成测试而苦恼。您将遇到有关该示例的可用问题,该示例没有为您提供 application.class 来配置集成测试,并且您对需要将 "where" 放入哪些 spring 引导注释一无所知测试到最后运行没有爆炸。
所以现在我给你一组最小的 3 classes(实体 + 存储库 + SpringbootTest)应该有你需要的 100% 的配置。这将作为您将来需要进行的任何基于 JPA 的集成测试的基础,然后您可以交换您的实体和存储库,并继续使用相同类型的 srpingboot 配置进行测试。
我先给你一些不相关的 classes。始终相同的东西,您要测试的东西,与配置无关。
我指的是存储库 + 实体。
在 eclipse 中创建你的 java 包:
tutorial.www.baeldung.com.tutorial001jpa.separateDS
将以下琐碎的实体和存储库转储到此包中 classes,它们基于我在上面提供的教程参考。
Tutorial001GenericEntity
package tutorial.www.baeldung.com.tutorial001jpa.separateDS;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "TUTORIAL_001_GENERIC_ENTITY")
public class Tutorial001GenericEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String value;
public Tutorial001GenericEntity() {
super();
}
public Tutorial001GenericEntity(String value) {
super();
this.value = value;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
// standard constructors, getters, setters
}
然后我们开始第二个简单的代码片段。
spring 存储库样板代码。
Tutorial001GenericEntityRepository
package tutorial.www.baeldung.com.tutorial001jpa.separateDS;
import org.springframework.data.jpa.repository.JpaRepository;
public interface Tutorial001GenericEntityRepository extends JpaRepository<Tutorial001GenericEntity, Long> {
}
此时你的maven工程,src/test/java一共有两个class。基本的东西。
一个实体和一个存储库,作为您需要执行的任何集成测试的示例。
所以现在你转到示例中唯一重要的class,总是会出现很多问题的东西,那就是spring引导测试class然后负责测试您的业务逻辑也有配置您的测试的复杂任务。
在这种情况下,此测试 class 具有允许 springboot 发现您的实体、存储库等的所有注释...
package tutorial.www.baeldung.com.tutorial001jpa.separateDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {
tutorial.www.baeldung.com.tutorial001jpa.separateDS.Tutorial001GenericEntityIntegrationTest.ConfigureJpa.class })
@SpringBootTest()
public class Tutorial001GenericEntityIntegrationTest {
@EntityScan(basePackageClasses = { Tutorial001GenericEntity.class })
@EnableJpaRepositories(basePackageClasses = Tutorial001GenericEntity.class)
@EnableAutoConfiguration()
public static class ConfigureJpa {
}
@Autowired
private Tutorial001GenericEntityRepository genericEntityRepository;
@Test
public void givenTutorial001GenericEntityRepository_whenSaveAndRetreiveEntity_thenOK() {
Tutorial001GenericEntity genericEntity = genericEntityRepository.save(new Tutorial001GenericEntity("test"));
Tutorial001GenericEntity foundEntity = genericEntityRepository.findOne(genericEntity.getId());
assertNotNull(foundEntity);
assertEquals(genericEntity.getValue(), foundEntity.getValue());
}
}
你看,重要的是这个 spring 启动测试有一个 class 级别的注释来为 spring 启动测试提供配置上下文。
我们正在做的是转储一个且只有一个 class 代表我们测试配置的引用。
tutorial.www.baeldung.com.tutorial001jpa.separateDS.Tutorial001GenericEntityIntegrationTest.ConfigureJpa.class
然后在这个小家伙身上,你把所有你需要的额外注释放在 springboot 提供的配置应用程序的世界中。
在这种情况下,我们有一个专门的注释来提及实体。
另一个提到存储库。
另一个告诉 springboot 激活它的自动配置。
此 spring 引导自动配置注释然后执行其他 vodoo,例如查看您的 class 路径并看到您在 class 路径中说:
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>test</scope>
<version>2.3.4</version>
</dependency>
并且它会立即知道如何为该数据库配置内存数据源。
在幕后,可能正在使用其他配置。
例如,如果您在 src/test/resources 中创建了一个 application.properties 文件,该文件将被考虑。
很高兴看到 appliction.properties 被你的 运行ning 测试考虑了。
如果你想验证这一点,请确保在你的测试设置中你没有,例如,对 postgres 的 JDBC 驱动程序的任何依赖。
然后在你的 application.properties 中输入以下内容:
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
这个方言与HSQL或H2不兼容,所以它会立即让你的绿色通过集成测试炸毁。
老实说,我不知道是否有更简单的注释组合来正确配置spring引导扫描以进行集成测试。
通常,我建议您尽量避免在 src/test/resources 中使用数十万个配置 classes。
因为如果在某个时候你想将所有集成测试从使用 applicat-postgres.proeprties 切换到 application-hsql.properties,你可能会发现自己需要调整多个配置 classes 而不仅仅是一.
因此,作为规则,对于您编写的每个 maven 组件,我会尝试让检查存储库的测试扩展某种 MyBaseINtegrationTestClass,并将此
放在那里
@ContextConfiguration(classes = {
tutorial.www.baeldung.com.tutorial001jpa.separateDS.Tutorial001GenericEntityIntegrationTest.ConfigureJpa.class })
让你只需要玩一个配置来测试孔项目。
无论如何,希望此处给出的 classes 的三元组对您有所帮助。
一件好事,对于用于集成测试的 Maven 依赖项,这是我正在使用的:
<!-- Test Dependencies JPA REPOSITORY TESTS -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
我使用 hsql 和 h2 的原因是因为我希望我的集成测试能够调整为使用 application-hsql 或 application-h2.properties.
有人知道为什么它不起作用吗?
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.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
我在 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;
}
}
我为 spring 定义了我的 datasource
条目定义:
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();
}
}
我的db-config.properties
是:
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
是:
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);
}
我尝试使用 spring 的 CrudRepository
接口将 CSV 文件写入数据库:
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();
}
}
}
我不使用 main
class 但 class 扩展 SpringBootServletInitializer
因为我想 运行 它在两个独立 tomcat 和 Tomcat 安装为 WAR 应用程序
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);
}
}
所有属性文件都在 classpath 资源中,因为它是一个 maven 项目。
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>
什么问题,为什么当我 运行 WebApplicationAware class 时它找不到 JPA 实体?
Spring 未找到任何 JPA 实体,因此未创建 JPA 元模型,这就是您遇到异常的原因。
此问题的原因可能是您的 class 路径上的错误持久性-api 版本。
您正在使用
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
但我很确定您的 spring 版本使用持久性-api 版本 2。
难道您使用的是版本 1 中的 @Entity 注释? 在运行时 spring 使用版本 2,这仅使用版本 2 中的@Entity 搜索实体!
删除依赖项
<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>
改为添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
这将为您提供正确版本的所有 JPA 依赖项。
我加了2个注解就解决了
@EnableAutoConfiguration
@EntityScan(basePackages = { "com.wt.rds" })
我的依赖项在 gradle
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.0.4.RELEASE'
不幸的是,大多数关于 JPA 集成测试的 spring 引导指南经常缺少一些配置。
所以这是一个希望对你有用的例子。
第 1 点。 我的本地环境当前设置为使用 spring 引导版本:
<version.spring.boot>1.5.9.RELEASE</version.spring.boot>
也就是说,我目前正在设置我的本地环境,以便能够 运行 针对多个数据库(例如 postgres、hsql、h2)进行集成测试。 因此,我首先在谷歌上搜索任何解决这个问题的随机教程。
下一个 link 就是这样一个例子:
https://www.baeldung.com/spring-testing-separate-data-source
上面的例子是一个很好的起点。它允许您获取一个有效的实体和一个有效的存储库。另一方面,spring 引导测试 class 本身还有很多不足之处。
通过上面的示例,您将立即为集成测试而苦恼。您将遇到有关该示例的可用问题,该示例没有为您提供 application.class 来配置集成测试,并且您对需要将 "where" 放入哪些 spring 引导注释一无所知测试到最后运行没有爆炸。
所以现在我给你一组最小的 3 classes(实体 + 存储库 + SpringbootTest)应该有你需要的 100% 的配置。这将作为您将来需要进行的任何基于 JPA 的集成测试的基础,然后您可以交换您的实体和存储库,并继续使用相同类型的 srpingboot 配置进行测试。
我先给你一些不相关的 classes。始终相同的东西,您要测试的东西,与配置无关。 我指的是存储库 + 实体。
在 eclipse 中创建你的 java 包: tutorial.www.baeldung.com.tutorial001jpa.separateDS
将以下琐碎的实体和存储库转储到此包中 classes,它们基于我在上面提供的教程参考。
Tutorial001GenericEntity
package tutorial.www.baeldung.com.tutorial001jpa.separateDS;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "TUTORIAL_001_GENERIC_ENTITY")
public class Tutorial001GenericEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String value;
public Tutorial001GenericEntity() {
super();
}
public Tutorial001GenericEntity(String value) {
super();
this.value = value;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
// standard constructors, getters, setters
}
然后我们开始第二个简单的代码片段。 spring 存储库样板代码。
Tutorial001GenericEntityRepository
package tutorial.www.baeldung.com.tutorial001jpa.separateDS;
import org.springframework.data.jpa.repository.JpaRepository;
public interface Tutorial001GenericEntityRepository extends JpaRepository<Tutorial001GenericEntity, Long> {
}
此时你的maven工程,src/test/java一共有两个class。基本的东西。 一个实体和一个存储库,作为您需要执行的任何集成测试的示例。
所以现在你转到示例中唯一重要的class,总是会出现很多问题的东西,那就是spring引导测试class然后负责测试您的业务逻辑也有配置您的测试的复杂任务。
在这种情况下,此测试 class 具有允许 springboot 发现您的实体、存储库等的所有注释...
package tutorial.www.baeldung.com.tutorial001jpa.separateDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {
tutorial.www.baeldung.com.tutorial001jpa.separateDS.Tutorial001GenericEntityIntegrationTest.ConfigureJpa.class })
@SpringBootTest()
public class Tutorial001GenericEntityIntegrationTest {
@EntityScan(basePackageClasses = { Tutorial001GenericEntity.class })
@EnableJpaRepositories(basePackageClasses = Tutorial001GenericEntity.class)
@EnableAutoConfiguration()
public static class ConfigureJpa {
}
@Autowired
private Tutorial001GenericEntityRepository genericEntityRepository;
@Test
public void givenTutorial001GenericEntityRepository_whenSaveAndRetreiveEntity_thenOK() {
Tutorial001GenericEntity genericEntity = genericEntityRepository.save(new Tutorial001GenericEntity("test"));
Tutorial001GenericEntity foundEntity = genericEntityRepository.findOne(genericEntity.getId());
assertNotNull(foundEntity);
assertEquals(genericEntity.getValue(), foundEntity.getValue());
}
}
你看,重要的是这个 spring 启动测试有一个 class 级别的注释来为 spring 启动测试提供配置上下文。
我们正在做的是转储一个且只有一个 class 代表我们测试配置的引用。 tutorial.www.baeldung.com.tutorial001jpa.separateDS.Tutorial001GenericEntityIntegrationTest.ConfigureJpa.class
然后在这个小家伙身上,你把所有你需要的额外注释放在 springboot 提供的配置应用程序的世界中。
在这种情况下,我们有一个专门的注释来提及实体。 另一个提到存储库。 另一个告诉 springboot 激活它的自动配置。
此 spring 引导自动配置注释然后执行其他 vodoo,例如查看您的 class 路径并看到您在 class 路径中说:
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>test</scope>
<version>2.3.4</version>
</dependency>
并且它会立即知道如何为该数据库配置内存数据源。
在幕后,可能正在使用其他配置。 例如,如果您在 src/test/resources 中创建了一个 application.properties 文件,该文件将被考虑。 很高兴看到 appliction.properties 被你的 运行ning 测试考虑了。
如果你想验证这一点,请确保在你的测试设置中你没有,例如,对 postgres 的 JDBC 驱动程序的任何依赖。 然后在你的 application.properties 中输入以下内容:
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
这个方言与HSQL或H2不兼容,所以它会立即让你的绿色通过集成测试炸毁。
老实说,我不知道是否有更简单的注释组合来正确配置spring引导扫描以进行集成测试。
通常,我建议您尽量避免在 src/test/resources 中使用数十万个配置 classes。 因为如果在某个时候你想将所有集成测试从使用 applicat-postgres.proeprties 切换到 application-hsql.properties,你可能会发现自己需要调整多个配置 classes 而不仅仅是一.
因此,作为规则,对于您编写的每个 maven 组件,我会尝试让检查存储库的测试扩展某种 MyBaseINtegrationTestClass,并将此
放在那里@ContextConfiguration(classes = {
tutorial.www.baeldung.com.tutorial001jpa.separateDS.Tutorial001GenericEntityIntegrationTest.ConfigureJpa.class })
让你只需要玩一个配置来测试孔项目。
无论如何,希望此处给出的 classes 的三元组对您有所帮助。
一件好事,对于用于集成测试的 Maven 依赖项,这是我正在使用的:
<!-- Test Dependencies JPA REPOSITORY TESTS -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
我使用 hsql 和 h2 的原因是因为我希望我的集成测试能够调整为使用 application-hsql 或 application-h2.properties.