Spring 引导 - 不是托管类型

Spring boot - Not a managed type

我使用 Spring boot+JPA 并在启动服务时遇到问题。

Caused by: java.lang.IllegalArgumentException: Not an managed type: class com.nervytech.dialer.domain.PhoneSettings
    at org.hibernate.jpa.internal.metamodel.MetamodelImpl.managedType(MetamodelImpl.java:219)
    at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.<init>(JpaMetamodelEntityInformation.java:68)
    at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getMetadata(JpaEntityInformationSupport.java:65)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:145)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:89)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:69)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:177)
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:239)
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:225)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:92)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1625)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1562)

这是 Application.java 文件,

@Configuration
@ComponentScan
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
@SpringBootApplication
public class DialerApplication {

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

我使用 UCp 作为连接池,DataSource 配置如下,

@Configuration
@ComponentScan
@EnableTransactionManagement
@EnableAutoConfiguration
@EnableJpaRepositories(entityManagerFactoryRef = "dialerEntityManagerFactory", transactionManagerRef = "dialerTransactionManager", basePackages = { "com.nervy.dialer.spring.jpa.repository" })
public class ApplicationDataSource {

    /** The Constant LOGGER. */
    private static final Logger LOGGER = LoggerFactory
            .getLogger(ApplicationDataSource.class);

    /** The Constant TEST_SQL. */
    private static final String TEST_SQL = "select 1 from dual";

    /** The pooled data source. */
    private PoolDataSource pooledDataSource;

UserDetailsS​​ervice 实现,

@Service("userDetailsService")
@SessionAttributes("user")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;

服务层实现,

@Service
public class PhoneSettingsServiceImpl implements PhoneSettingsService {

}

存储库 class,

@Repository
public interface PhoneSettingsRepository extends JpaRepository<PhoneSettings, Long> {

}

实体class,

@Entity
@Table(name = "phone_settings", catalog = "dialer")
public class PhoneSettings implements java.io.Serializable {

WebSecurityConfig class,

@Configuration
@EnableWebMvcSecurity
@ComponentScan
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    /**
     * Instantiates a new web security config.
     */
    public WebSecurityConfig() {

        super();
    }

    /**
     * {@inheritDoc}
     * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
            .antMatchers("/login", "/logoffUser", "/sessionExpired", "/error", "/unauth", "/redirect", "*support*").permitAll()
            .anyRequest().authenticated().and().rememberMe().and().httpBasic()
            .and()
            .csrf()
            .disable().logout().deleteCookies("JSESSIONID").logoutSuccessUrl("/logoff").invalidateHttpSession(true);
    }


    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {

      auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

}

套餐如下,

  1. Application class 在 - com.nervy.dialer
  2. Datasource class 在 - com.nervy.dialer.common
  3. 实体 classes 在 - com.nervy.dialer.domain
  4. 服务 classes 在 - com.nervy.dialer.domain.service.impl
  5. 控制器在 - com.nervy.dialer.spring.controller
  6. 存储库 classes 在 - com.nervy.dialer.spring.jpa.repository
  7. WebSecurityConfig 在 - com.nervy.dialer.spring.security

谢谢

我认为将 @ComponentScan 替换为 @ComponentScan("com.nervy.dialer.domain") 会奏效。

编辑:

我添加了一个 sample application 来演示如何使用 BoneCP 设置池化数据源连接。

该申请与您的申请结构相同。我希望这将帮助您解决您的配置问题

在 Spring 引导入口点 class.

中使用 @EntityScan 配置实体的位置

2016 年 9 月更新:对于 Spring Boot 1.4+:
使用 org.springframework.boot.autoconfigure.domain.EntityScan
而不是 org.springframework.boot.orm.jpa.EntityScan,因为 ...boot.orm.jpa.EntityScan 从 Spring Boot 1.4

开始是

尝试添加以下所有内容,在我的应用程序中它与 tomcat

一起工作正常
 @EnableJpaRepositories("my.package.base.*")
 @ComponentScan(basePackages = { "my.package.base.*" })
 @EntityScan("my.package.base.*")   

我正在使用 spring 启动,当我使用嵌入式 tomcat 时它在没有 @EntityScan("my.package.base.*") 的情况下工作正常但是当我尝试将应用程序部署到外部 tomcat 我的实体出现 not a managed type 错误。

补充阅读:

@ComponentScan 用于扫描标记为 @Controller, @Service, @Repository, @Component 等的所有组件……

其中 @EntityScan 用于扫描您的所有实体,这些实体被标记为 @Entity 用于您的应用程序中任何已配置的 JPA。

在我的例子中,问题是由于我忘记用 @javax.persistence.Entity 注释对我的实体 类 进行注释。呸!

//The class reported as "not a amanaged type"
@javax.persistence.Entity
public class MyEntityClass extends my.base.EntityClass {
    ....
}

您可以使用 @EntityScan 注释并提供实体包以扫描所有 jpa 实体。您可以在使用 @SpringBootApplication 注释的基础应用程序 class 上使用此注释。

例如 @EntityScan("com.test.springboot.demo.entity")

您可能错过了 class 定义中的 @Entity,或者您有明确的组件扫描路径并且此路径不包含您的 class

我有同样的问题,版本 spring boot v1.3.x 我所做的是将 spring 引导升级到版本 1.5.7.RELEASE。然后问题就解决了。

我正在使用 spring boot 2.0,我通过将 @ComponentScan 替换为 @EntityScan

来修复此问题

永远不要忘记添加 @实体 在域 class

我遇到这个问题是因为我没有映射 orm.xml 文件

中的所有实体

如果您配置自己的 EntityManagerFactory Bean,或者如果您从另一个项目复制粘贴了这样的持久性配置,则必须在 EntityManagerFactory 的配置中设置或调整包:

@Bean
public EntityManagerFactory entityManagerFactory() throws PropertyVetoException {
    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
    vendorAdapter.setGenerateDdl(true);
    LocalContainerEntityManagerFactoryBean factory;
    factory = new LocalContainerEntityManagerFactoryBean();
    factory.setPackagesToScan("!!!!!!package.path.to.entities!!!!!");
    //...
}

注意“多重”需求,您需要一个字符串数组作为传递给 setPackagesToScan 的参数(而不是逗号分隔的单字符串值)。下面说明了这个问题。

    String[] packagesArray = "com.mypackage1,com.mypackage2".split(",");
    em.setPackagesToScan(packagesArray);

下面对我有用..

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.apache.catalina.security.SecurityConfig;
import org.junit.Before;
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.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import com.something.configuration.SomethingConfig;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { SomethingConfig.class, SecurityConfig.class }) //All your configuration classes
@EnableAutoConfiguration
@WebAppConfiguration // for MVC configuration
@EnableJpaRepositories("com.something.persistence.dataaccess")  //JPA repositories
@EntityScan("com.something.domain.entity.*")  //JPA entities
@ComponentScan("com.something.persistence.fixture") //any component classes you have
public class SomethingApplicationTest {

    @Autowired
    private WebApplicationContext ctx;
    private MockMvc mockMvc;

    @Before
    public void setUp() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx).build();
    }

    @Test
    public void loginTest() throws Exception {
        this.mockMvc.perform(get("/something/login"))
        .andDo(print()).andExpect(status().isOk());
    }

}

出现这个错误是因为我写的很蠢

public interface FooBarRepository extends CrudRepository<FooBarRepository, Long> { ...

简要说明:通常创建一个 FooBarRepository class 来管理 FooBar 对象(通常在 table 中表示数据,称为 foo_bar。)当扩展 CrudRepository 以创建专门的存储库 class,需要指定正在管理的类型——在本例中为 FooBar。不过,我错误输入的是 FooBarRepository 而不是 FooBar。 FooBarRepository 不是我尝试使用 FooBarRepository 管理的类型(class)。因此,编译器会发出此错误。

我在 粗体 中突出显示了错误输入。在我的示例中删除突出显示的单词 Repository,然后代码编译。

我遇到了同样的问题,但只有在 运行 spring 引导测试需要 JPA 的情况下才会出现。最终结果是我们自己的 jpa 测试配置正在初始化一个 EntityManagerFactory 并设置要扫描的包。如果您手动设置,这显然会覆盖 EntityScan 参数。

    final LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter( vendorAdapter );
    factory.setPackagesToScan( Project.class.getPackage().getName());
    factory.setDataSource( dataSource );

重要提示:如果你仍然卡住你应该在setPackagesToScan()方法的org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager中设置一个断点并查看在哪里调用它以及将什么包传递给它。

将其放入您的 Application.java 文件

@ComponentScan(basePackages={"com.nervy.dialer"})
@EntityScan(basePackages="domain")

我在从 Spring boot 1.3.x 迁移到 1.5 时遇到了一些问题,我在 EntityManagerFactory bean

更新了实体包后就开始工作了
  @Bean(name="entityManagerFactoryDef")
  @Primary
  public LocalContainerEntityManagerFactoryBean defaultEntityManager() {
      Map map = new HashMap();
      map.put("hibernate.default_schema", env.getProperty("spring.datasource.username"));
      map.put("hibernate.hbm2ddl.auto", env.getProperty("spring.jpa.hibernate.ddl-auto"));
      LocalContainerEntityManagerFactoryBean em = createEntityManagerFactoryBuilder(jpaVendorProperties())
              .dataSource(primaryDataSource()).persistenceUnit("default").properties(map).build();
      em.setPackagesToScan("com.simple.entity");
      em.afterPropertiesSet();
      return em;
  }

此 bean 在应用程序 class 中引用如下

@SpringBootApplication
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactoryDef")
public class SimpleApp {

}

我已将我的应用程序 class 移至父包,例如:

主要class:com.job.application

实体:com.job.application.entity

这样您就不必添加@EntityScan

遇到了类似的问题。 在我的例子中,存储库和被管理的类型不在同一个包中。

如果您使用多模块 SPRING 数据 JPA 项目。

如果您使用多个模块并且它们具有 Jpa 实体和存储库。这可能对你有用。我曾经在外部 tomcat 上部署时遇到“不是托管类型”错误(在嵌入式 tomcat 中从未遇到过)。

我有 1 个主模块和 2 个其他模块作为依赖项。将主项目部署为 war 时,我可以看到总共有 3 个 Spring 应用程序正在初始化。当执行顺序是先主模块后子模块时,没有报错。但有时,子模块通常会在主模块之前被调用。这曾经导致“不是托管类型实体异常”

棘手的是,错误不会出现在 spring 嵌入式启动 tomcat 中。但是当我们将它部署在外部时 tomcat。这个异常过去太随机了。我必须多次部署相同的 war 才能获得正确的顺序。

我花了一整天的时间来解决这个问题。但事实证明,问题出在我将其他模块作为依赖项添加到主模块中的方式。如果您要将 spring 引导模块添加为另一个项目中的依赖项,请确保 jar 中未涉及主要 class。当您将另一个 spring 引导项目作为依赖项并且尝试将该项目部署为 war 时。无法保证主应用程序 class 的执行顺序。去掉mainclass,基本上就避免了子模块的独立执行。因此,不会有任何执行顺序问题的余地。

对于未来的读者:

这里是多包扫描的语法糖。

请注意,我的两个包也位于不同的 jar 中,但包是主要驱动程序。只是记下我的 2 jar 情况。

    em.setPackagesToScan(new String[] {"com.package.ONE.jpa.entities" , "com.package.TWO.jpa.entities"});

我原来的错误代码如下:

    em.setPackagesToScan("com.package.ONE.jpa.entities, com.package.TWO.jpa.entities");

让我失望的是我的“xml 到 java-config swapover”。下面显示了一个简单的逗号分隔值。

逗号分隔的列表似乎适用于 di.xml,但不适用于“java 配置”。

Java,它是:::“什么时候是逗号分隔,什么时候是字符串数组,什么时候是字符串可变参数”。...... jig saw puzzle drives有时我会发疯。

    <!-- the value of "id" attribute below MUST BE "entityManagerFactory"  spring-data voodoo -->
    <bean id="entityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="packagesToScan" value="com.package.ONE.jpa.entities, com.package.TWO.jpa.entities"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="${spring.jpa.show-sql}"/>
                <property name="generateDdl" value="${spring.jpa.generate-ddl}"/>
            </bean>
        </property>
        <!-- See  -->
        <property name="jpaProperties">
            <props>
                <prop key="hibernate.hbm2ddl.auto">${spring.jpa.hibernate.ddl-auto}</prop>
                <prop key="hibernate.dialect">${spring.jpa.properties.hibernate.dialect}</prop>
            </props>
        </property>
    </bean>

我认为没有人提到它,但值得注意的是 Not a managed type 错误也可能是由包裹字母大小写引起的。 例如,如果要扫描的包被称为 myEntities,而我们在包扫描配置中提供 myentities,那么它可能在一台机器上工作,而在另一台机器上不工作,所以要小心字母大小写。

将包添加到 @EntityScan 对我来说没有帮助,因为有一个指定包的工厂 bean,所以必须在那里添加一个额外的条目。然后它开始工作了。

不要像我一样犯明显的错误,弄错模板类型的顺序。确保您没有在模板声明中首先使用 id,例如:

public interface CapacityBasedProductRepository extends JpaRepository<Long, CapacityBasedProduct> {
}

首先是 JPA class,然后是 id 列类型,如下所示:

public interface CapacityBasedProductRepository extends JpaRepository<CapacityBasedProduct, Long> {
}

否则你会得到它抱怨 java.lang.Long 是一个未知的实体类型。它使用第一项来查找要使用的实体。

解决这个问题的另一种方法是...包含 @SpringBootApplication 的 class 包应该是根包,所有其他包应该是子包。例如:

package com.home

@SpringBootApplication
public class TestApplication{
  springapplication.run....
}
 
package com.home.repo

@Repository
public interface StudentRepo implements JpaRepository<Student, int>{
 ..........
}

package com.home.entity

@Entity
@Table(name = "student")
public class Student{
 ..........
}

我用 Not a managed type 重现了类似的问题。

更具体地说:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'stockPriceRepository' defined in com.example.stockclient.repository.StockPriceRepository defined in @EnableJpaRepositories declared on StockUiApplication: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class com.example.stockclient.StockPrice

由于我有 multi-module 项目,我需要为 Spring Data JPA 启用自动配置支持以了解 JPA 存储库的路径,因为默认情况下,它只会扫描用于检测 JPA 个存储库的主应用程序包及其子包。

所以在我的特定用例中,我已经使用 @EnableJpaRepositories 来启用 JPA 存储库,其中包含必要的包,但没有 @EntityScan.

@EntityScan@EnableJpaRepositories 的情况相同,因为实体 类 由于 multi-module 项目而未放置在主应用程序包中。

更多细节你可以参考,比如这个article

就我而言,我错误地从 jakarta.persistence-api 导入了 类。

javax.persistence.* 导入对我有用:

package foo;
import javax.persistence.Entity;

@Entity
@Table(name = "phone_settings", catalog = "dialer")
public class PhoneSettings implements java.io.Serializable {
   // ...
}

如果您将 SessionFactory 用作 EMF:

在我的例子中,问题是我忘记包含新的实体类型,为此我得到了错误,作为 Hibernate 配置中带注释的 class。

所以,在您的 SessionFactory bean 中,不要忘记为您的新实体类型包含这一行:

configuration.addAnnotatedClass(MyEntity.class);