admin管理员组

文章数量:1356219

When using @EntityScan all entities in the specified basePackages are picked up by the scan. For certain reasons, we would like to avoid that.

We had the idea to replace the @EntityScan annotation with manual configuration to include only specific entity classes, instead of every entity in the package, by calling factoryBean.setManagedTypes(managedTypes).

There is one baeldung post that goes into this direction, but the focus is not on setting single managedTypes.

Actually, we were surprised that we couldn't find any blogpost, stackoverflow question, etc. dealing with the question "Is it possible to configure @EntityScan or an alternative to only track single classes, not full packages?".

Are there any issues or drawbacks in doing so, in a Spring Boot application?

The javadoc of LocalContainerEntityManagerFactoryBean.setManagedTypes even says:

Set the PersistenceManagedTypes to use to build the list of managed types as an alternative to entity scanning. -- ...orm/jpa/LocalContainerEntityManagerFactoryBean

I created an example project to demonstrate the idea:

From the configuration class JpaConfig:

@Configuration
public class JpaConfig {

List<String> sharedManagedEntityClassNames = Stream.of(
            external.model.Book.class // located in a 'remote' package, outside of .example.bookstore
    ).map(Class::getName).toList();

List<String> managedEntityPackages = List.of(
            ".example.bookstore.model"
    );

@Bean(name = "persistenceManagedTypes")
PersistenceManagedTypes persistenceManagedTypes(ResourceLoader resourceLoader) {
    var scanResult = new PersistenceManagedTypesScanner(resourceLoader)
            .scan(managedEntityPackages.toArray(new String[0]));

    // merge the shared managed entity class names with the scanned ones
    List<String> managedClassNames = new ArrayList<>();
    managedClassNames.addAll(sharedManagedEntityClassNames);
    managedClassNames.addAll(scanResult.getManagedClassNames());

    return PersistenceManagedTypes.of(managedClassNames, Collections.emptyList());
}

// entityManagerFactoryBean

}

This is how we override the entityManagerFactory bean:

@Bean(name = "entityManagerFactory")
public EntityManagerFactory entityManagerFactory(DataSource dataSource,
                                                 PersistenceManagedTypes managedTypes) {
    LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
    factoryBean.setDataSource(dataSource);
    factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

    factoryBean.setManagedTypes(managedTypes); // alternative to @EntityScan and @EnableJpaRepositories

    factoryBean.afterPropertiesSet();
    return factoryBean.getObject();
}

We do not feel too confident about creating our own emFactory bean, as this is the backbone of every JPA related logic in a JPA-based application:

  1. We are uncertain if it could create additional maintenance effort in case the framework implementations (Spring Boot or Hibernate) change here.
  2. Since this is a critical component with rather complex configuration settings, we are somewhat afraid of overseeing something and break the application's persistence layer.

Any advice or experience with using setManagedTypes(PersistenceManagedTypes) instead of @EntityScan?

Thanks!

When using @EntityScan all entities in the specified basePackages are picked up by the scan. For certain reasons, we would like to avoid that.

We had the idea to replace the @EntityScan annotation with manual configuration to include only specific entity classes, instead of every entity in the package, by calling factoryBean.setManagedTypes(managedTypes).

There is one baeldung post that goes into this direction, but the focus is not on setting single managedTypes.

Actually, we were surprised that we couldn't find any blogpost, stackoverflow question, etc. dealing with the question "Is it possible to configure @EntityScan or an alternative to only track single classes, not full packages?".

Are there any issues or drawbacks in doing so, in a Spring Boot application?

The javadoc of LocalContainerEntityManagerFactoryBean.setManagedTypes even says:

Set the PersistenceManagedTypes to use to build the list of managed types as an alternative to entity scanning. -- https://docs.spring.io...orm/jpa/LocalContainerEntityManagerFactoryBean

I created an example project to demonstrate the idea: https://github/frankkriegl-zero/jpa-managed-types-sample/tree/stackoverflow-question-79541611

From the configuration class JpaConfig:

@Configuration
public class JpaConfig {

List<String> sharedManagedEntityClassNames = Stream.of(
            external.model.Book.class // located in a 'remote' package, outside of .example.bookstore
    ).map(Class::getName).toList();

List<String> managedEntityPackages = List.of(
            ".example.bookstore.model"
    );

@Bean(name = "persistenceManagedTypes")
PersistenceManagedTypes persistenceManagedTypes(ResourceLoader resourceLoader) {
    var scanResult = new PersistenceManagedTypesScanner(resourceLoader)
            .scan(managedEntityPackages.toArray(new String[0]));

    // merge the shared managed entity class names with the scanned ones
    List<String> managedClassNames = new ArrayList<>();
    managedClassNames.addAll(sharedManagedEntityClassNames);
    managedClassNames.addAll(scanResult.getManagedClassNames());

    return PersistenceManagedTypes.of(managedClassNames, Collections.emptyList());
}

// entityManagerFactoryBean

}

This is how we override the entityManagerFactory bean:

@Bean(name = "entityManagerFactory")
public EntityManagerFactory entityManagerFactory(DataSource dataSource,
                                                 PersistenceManagedTypes managedTypes) {
    LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
    factoryBean.setDataSource(dataSource);
    factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

    factoryBean.setManagedTypes(managedTypes); // alternative to @EntityScan and @EnableJpaRepositories

    factoryBean.afterPropertiesSet();
    return factoryBean.getObject();
}

We do not feel too confident about creating our own emFactory bean, as this is the backbone of every JPA related logic in a JPA-based application:

  1. We are uncertain if it could create additional maintenance effort in case the framework implementations (Spring Boot or Hibernate) change here.
  2. Since this is a critical component with rather complex configuration settings, we are somewhat afraid of overseeing something and break the application's persistence layer.

Any advice or experience with using setManagedTypes(PersistenceManagedTypes) instead of @EntityScan?

Thanks!

Share Improve this question edited Mar 31 at 8:21 franok asked Mar 28 at 13:49 franokfranok 455 bronze badges 3
  • The reason/motivation behind this question: In a modulith (modular monolith) project there might be shared entities that are used by multiple modules. The modules share a database, but not every module must have access to every table, while some tables need to be accessed by multiple modules. This is enforced by different DB Users per module and respective DB User Grants. Problem is, that the entity scan includes all entities in the specified package. If an entity is included in the entity scan for which the module doesn't have table access permission, the app will fail at runtime. – franok Commented Mar 28 at 13:54
  • One workaround is to put every entity class into a dedicated package and specify every entity-package on a fine-grained level in the entity scan annotation. This is somewhat cumbersome - a lot of boilerplate code and useless packages are created. Hence we came up with the idea of using setManagedTypes(PersistenceManagedTypes) – franok Commented Mar 28 at 13:54
  • based on the answer by @andrey-b-panfilov I updated the example project with the suggested solution: github/frankkriegl-zero/jpa-managed-types-sample (original code for the question is now on a separate branch) – franok Commented Mar 31 at 12:51
Add a comment  | 

1 Answer 1

Reset to default 1

If my memory serves me right that could be implemented using following way:

@Bean
public HibernatePropertiesCustomizer extraEntities() {
    return map -> {
        List<Class> extra = new ArrayList<>();
        extra.add(MyEntity.class);
        var existing = map.get(AvailableSettings.LOADED_CLASSES);
        if (existing != null) {
            extra.addAll((Collection<Class>) existing);
        }
        map.put(AvailableSettings.LOADED_CLASSES, extra);
    };
}

本文标签: