9
votes

I am getting this error in my code.

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'roleRepository': Cannot create inner bean '(inner bean)#7540dc57' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#7540dc57': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available

I saw these:

Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument

NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available

NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' is defined

None of them answers my question. The thing is I was able the solve the problem but I have a question about it.

Let me share my related code and then ask the question I have.

@Configuration
@EnableTransactionManagement 
public class HibernateConfig {

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerF() {
    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource(dataSource());
    em.setPackagesToScan(new String[] {"com.gitreporter"});
    JpaVendorAdapter jpaAdapter = new HibernateJpaVendorAdapter();
    em.setJpaVendorAdapter(jpaAdapter);
    em.setJpaProperties(jpaProperties());

    return em;
}

@Bean
public PlatformTransactionManager jpaTransactionManager(EntityManagerFactory emf) {
    JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
    jpaTransactionManager.setEntityManagerFactory(emf);

    return jpaTransactionManager;
}

private final Properties jpaProperties() {
    Properties properties = new Properties();
    properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");

    return properties;
}


@Bean
public DataSource dataSource() {
    BasicDataSource dataSource = new BasicDataSource();
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/MyDBNAME?useSSL=false");
    dataSource.setUsername("username");
    dataSource.setPassword("password");

    return dataSource;
}

The problem was on this line:

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerF() {

I changed the medhod name to entityManagerFactory like so:

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

Making the name of the factory bean in the context equal to "entityManagerFactory" since by default the name of the bean will be equal to the method name unless explicitly specified.

My question: Is there a place in JPA API that "by convention" it is looking for an EntityManagerFactory bean named "entityManagerFactory" inside Spring container? Why is it not working when the name of the method is "entityManagerF"?

Here is the rest of the code:

@NoRepositoryBean
public interface GenericRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {

public List<T> findByAttributeContainsText(String attributeName, String text);

}

public class GenericRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
    implements GenericRepository<T, ID> {

private EntityManager entityManager;

public GenericRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
    super(entityInformation, entityManager);
    this.entityManager = entityManager;
 }
}


public interface RoleRepository extends GenericRepository<Role, Long> {

}
2

2 Answers

3
votes

I found the answer.

Checkout the documentation for @EnableJpaRepositories annotation.

In the optional elements you will see this:

entityManagerFactoryRef Configures the name of the EntityManagerFactory bean definition to be used to create repositories discovered through this annotation.

Go down the page to the details and you will see this:

entityManagerFactoryRef

public abstract String entityManagerFactoryRef

Configures the name of the EntityManagerFactory bean definition to be used to create repositories discovered through this annotation. Defaults to entityManagerFactory.

Returns:

Default: "entityManagerFactory"

So this "conventional" default configuration comes from @EnableJpaRepositories annotation itself.

0
votes

Yes I believe so. In your case, Spring did have pre-configured a bean entityManagerFactory.

An excerpt from the javadoc for @EnableAutoConfiguration says

        Enable auto-configuration of the Spring Application Context, attempting to guess and configure beans that you are likely to need. Auto-configuration classes are usually applied based on your classpath and what beans you have defined. For example, if you have tomcat-embedded.jar on your classpath you are likely to want a TomcatServletWebServerFactory (unless you have defined your own ServletWebServerFactory bean).

And, seeing your hibernate configuration,

private final Properties jpaProperties() {
    Properties properties = new Properties();
    properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");

    return properties;

... I reckon, this must get invoked, which is derived from JpaBaseConfiguration

@Configuration
@ConditionalOnSingleCandidate(DataSource.class)
class HibernateJpaConfiguration extends JpaBaseConfiguration {

And JpaBaseConfiguration did have a bean definition for entityManagerFactory, which is what you are trying to override.

@Bean
@Primary
@ConditionalOnMissingBean({ LocalContainerEntityManagerFactoryBean.class,
        EntityManagerFactory.class })
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
        EntityManagerFactoryBuilder factoryBuilder) {
    Map<String, Object> vendorProperties = getVendorProperties();
    customizeVendorProperties(vendorProperties);
    return factoryBuilder.dataSource(this.dataSource).packages(getPackagesToScan())
            .properties(vendorProperties).mappingResources(getMappingResources())
            .jta(isJta()).build();
}

Edit:- Thanks to OP's answer. So, it can even be used to provide a custom bean name through a declaration like

@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerF",
)         

Another stackoverflow thread at https://stackoverflow.com/a/45665826/5107365 provides a deeper insight into this issue.