Reputation: 1242
I want to add a custom bulk save method based on the code from JPA batch inserts with Hibernate & Spring Data to all Spring Data JPA repositories in my application. The Spring Data doc explains how this can be done with a custom repository base class like the one shown below. My question is how to set the batchSize
property in the example below. Injection with @Value as shown below does not work.
@NoRepositoryBean
public interface BulkRepository<T, ID extends Serializable>
extends JpaRepository<T, ID> {
void bulkSave(Iterable<T> entities);
}
public class BulkRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BulkRepository<T, ID> {
private final EntityManager entityManager;
@Value("${spring.jpa.properties.hibernate.jdbc.batch_size}")
private int batchSize;
public BulkRepositoryImpl(JpaEntityInformation entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityManager = entityManager;
}
public void bulkSave(Iterable<T> entities) {
// implementation using batchSize goes here
}
}
Do I have to use a custom JpaRepositoryFactoryBean
to build a configured BulkRepositoryImpl
? Or is there a more direct way?
Upvotes: 1
Views: 3131
Reputation: 346
You can get it from EntityManager.
String defaultBatchSize = "100";
String batchSizeString = (String) entityManager.getEntityManagerFactory().getProperties()
.getOrDefault("hibernate.jdbc.batch_size", defaultBatchSize);
batchSize = Integer.parseInt(batchSizeString);
Upvotes: 0
Reputation: 36
I came across the exact same problem and could not find any way around defining my own JpaRepositoryFactoryBean
class. It seems that the dependencies of the custom repository base class are not automatically injected the way they are in a standard bean (see here and here). Also, when creating instances of repository interfaces, the default JpaRepositoryFactory
only passes instances of JpaEntityInformation
and EntityManager
to the class constructor (see here). This, as far as I can tell, effectively prevents you from including additional dependencies for classes extending SimpleJpaRepository
.
I ended up defining the custom factory the following way:
@Configuration
@ConfigurationProperties(prefix = "spring.jpa.properties.hibernate.jdbc")
public class RepositoryConfiguration {
private int batchSize;
}
public class MyCustomRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> {
private RepositoryConfiguration repositoryConfiguration;
public MyCustomRepositoryFactoryBean(Class<? extends R> repositoryInterface, RepositoryConfiguration repositoryConfiguration) {
super(repositoryInterface);
this.repositoryConfiguration = repositoryConfiguration;
}
@Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new MyCustomRepositoryFactory(entityManager, repositoryConfiguration);
}
private static class MyCustomRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private RepositoryConfiguration repositoryConfiguration;
MyCustomRepositoryFactory(EntityManager entityManager, RepositoryConfiguration repositoryConfiguration) {
super(entityManager);
this.repositoryConfiguration = repositoryConfiguration;
}
@Override
@SuppressWarnings("unchecked")
protected SimpleJpaRepository<?, ?> getTargetRepository(RepositoryInformation information,
EntityManager entityManager) {
JpaEntityInformation<T, ?> entityInformation =
(JpaEntityInformation<T, ?>) getEntityInformation(information.getDomainType());
return new MyCustomRepositoryImpl<T, I>(
entityInformation,
entityManager,
repositoryConfiguration);
}
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return MyCustomRepositoryImpl.class;
}
}
}
While it is not possible to inject fields with @Value
in MyCustomRepositoryFactoryBean
either, Spring resolves the dependencies passed to the constructor, so you can just provide your properties through a bean (RepositoryConfiguration
in the code above) and pass it down to MyCustomRepositoryImpl
. Lastly, you will need to instruct Spring Data to use your FactoryBean
class when creating repositories by adding
@EnableJpaRepositories(
repositoryFactoryBeanClass = MyCustomRepositoryFactoryBean.class
)
to a @Configuration
annotated bean. It's a lot of boilerplate, but it works.
N.B. I am using spring-data-jpa:1.11.8.RELEASE
.
Upvotes: 2