Исключение Hibernate: не удалось напрямую создать экземпляр управляемого компонента

У меня есть случай, когда мне нужно два экземпляра EntityManager, один для чтения/записи и один для отслеживания аудита.

Мы создали два экземпляра Entity Manager, выполнив следующие действия:

// в App.java мы добавили следующую аннотацию:

@Configuration
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })

Затем создал два менеджера сущностей, например: 1:

package com.vw.asa.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.PersistenceContext;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@EnableTransactionManagement
@Configuration
@EnableJpaRepositories(basePackages = "com.vw.asa.repositories", entityManagerFactoryRef = "entityManager", transactionManagerRef = "transactionManager")
public class PrimaryDatasource {
    
    /**
     * Primary Datasource Configuration
     * @return
     */
    @Bean(name = "dataSource")      // 3
    @Primary
    @ConfigurationProperties(prefix = "primary.datasource.mysql")
    public DataSource mysqlDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @PersistenceContext(unitName = "primary")   // 4
    @Primary
    @Bean(name = "entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder.dataSource(mysqlDataSource())
                .persistenceUnit("primary")
                .properties(jpaProperties())
                .packages("com.vw.asa").build();
    }
    
    private Map<String, Object> jpaProperties() {
        Map<String, Object> props = new HashMap<>();
        props.put("hibernate.ejb.naming_strategy", new SpringPhysicalNamingStrategy());
        return props;
    }
    
}

2:

package com.vw.asa.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.PersistenceContext;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@EnableTransactionManagement
@Configuration
@EnableJpaRepositories(basePackages = "com.vw.asa.repositories", entityManagerFactoryRef = "secondaryMySqlEntityManager", transactionManagerRef = "secondaryTransactionManager")
public class SecondaryDatasource {
    
    /**
     * Secondary Datasource Configuration
     * @return
     */
    @Bean
    @ConfigurationProperties(prefix = "secondary.datasource.mysql")
    public DataSource mysqlDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @PersistenceContext(unitName = "secondary")
    @Bean(name = "secondaryMySqlEntityManager")
    public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder) {
        return  builder.dataSource(mysqlDataSource()).properties(jpaProperties()).persistenceUnit("secondary").packages("au.com.myblog.domain").build();
    }
    
    @Bean(name = "secondaryTransactionManager")
    public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
        JpaTransactionManager tm = new JpaTransactionManager();
        tm.setEntityManagerFactory(mySqlEntityManagerFactory(builder).getObject());
        tm.setDataSource(mysqlDataSource());
        return tm;
    }
    
    private Map<String, Object> jpaProperties() {
        Map<String, Object> props = new HashMap<>();
        // naming strategy to put underscores instead of camel case
        // as per auto JPA Configuration
        props.put("hibernate.ejb.naming_strategy", new SpringPhysicalNamingStrategy());
        props.put("hibernate.hbm2ddl.auto", "none");
        return props;
    }
    
}

Однако при запуске приложения возникает это исключение:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [com/vw/asa/config/PrimaryDatasource.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: primary] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.InstantiationException: Could not instantiate managed bean directly : com.vw.asa.listeners.AttachmentListener
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:595)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:330)
    ... 121 more
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: primary] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.InstantiationException: Could not instantiate managed bean directly : com.vw.asa.listeners.AttachmentListener
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:403)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:378)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1855)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1792)
    ... 128 more
Caused by: org.hibernate.InstantiationException: Could not instantiate managed bean directly : com.vw.asa.listeners.AttachmentListener
    at org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer.produceBeanInstance(FallbackBeanInstanceProducer.java:45)
    at org.hibernate.resource.beans.container.spi.FallbackContainedBean.<init>(FallbackContainedBean.java:23)
    at org.hibernate.resource.beans.internal.ManagedBeanRegistryImpl.getBean(ManagedBeanRegistryImpl.java:58)
    at org.hibernate.jpa.event.internal.CallbackBuilderLegacyImpl.resolveEntityCallbacks(CallbackBuilderLegacyImpl.java:200)
    at org.hibernate.jpa.event.internal.CallbackBuilderLegacyImpl.buildCallbacksForEntity(CallbackBuilderLegacyImpl.java:74)
    at org.hibernate.event.service.internal.EventListenerRegistryImpl.prepare(EventListenerRegistryImpl.java:146)
    at org.hibernate.boot.internal.MetadataImpl.initSessionFactory(MetadataImpl.java:376)
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:211)
    at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:468)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1237)
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:391)
    ... 132 more
Caused by: java.lang.NoSuchMethodException: com.vw.asa.listeners.AttachmentListener.<init>()
[2021-02-21 08:43:48,090] Artifact vw-asa:war exploded: Error during artifact deployment. See server log for details.
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.getDeclaredConstructor(Class.java:2178)
    at org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer.produceBeanInstance(FallbackBeanInstanceProducer.java:40)
    ... 144 more

Идея заключалась в том, что мы могли определить, какой EM использовать с данной транзакцией. Чем это вызвано? Он выдает разные ошибки, если я переименую его из EntityManagerFactory


person Barry Chapman    schedule 22.02.2021    source источник
comment
Причина ошибки в трассировке стека и, вероятно, не связана с несколькими источниками данных: spring Could not instantiate managed bean directly : com.vw.asa.listeners.AttachmentListener, потому что метод init не существует.   -  person AllirionX    schedule 22.02.2021
comment
@AllirionX Что вызывает инициализацию? Зачем нужен метод init?   -  person Barry Chapman    schedule 22.02.2021
comment
Бинам нужны конструкторы по умолчанию, если вы хотите, чтобы Spring их инициализировал. Вы можете прочитать этот очень похожий вопрос SO: > stackoverflow.com/questions/18023870/   -  person AllirionX    schedule 22.02.2021


Ответы (1)


Проблема в том, что ваш EntityListener не предоставляет конструктор по умолчанию (без аргументов), который требуется Hibernate для создания экземпляра прослушивателя.

Если вы используете Hibernate 5.3 или выше, вы можете заставить Spring предоставить эти bean-компоненты для Hibernate, установив BeanContainer в EntityManagerFactory следующим образом:

public LocalContainerEntityManagerFactoryBean mySqlEntityManagerFactory(EntityManagerFactoryBuilder builder, ConfigurableListableBeanFactory beanFactory) {
    LocalContainerEntityManagerFactoryBean emfb = builder.dataSource(mysqlDataSource())
            .persistenceUnit("primary")
            .properties(jpaProperties())
            .packages("com.vw.asa").build();
    emfb.getJpaPropertyMap().put(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory));
    return emfb;
}
person Rafael Ide    schedule 27.03.2021