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?
-
how do you set the "spring.jpa.properties.hibernate.jdbc.batch_size" value?Alain-Michel Chomnoue N– Alain-Michel Chomnoue N2017年08月02日 13:45:51 +00:00Commented Aug 2, 2017 at 13:45
-
"spring.jpa.properties.hibernate.jdbc.batch_size" is set in the application.properties. My question is how to get the value from there into the BulkRepositoryImpl instances.Michael Koch– Michael Koch2017年08月02日 13:47:54 +00:00Commented Aug 2, 2017 at 13:47
-
what is the error you get?Abdullah Mohammad Motiullah– Abdullah Mohammad Motiullah2017年08月02日 14:17:15 +00:00Commented Aug 2, 2017 at 14:17
-
The BulkRepositoryImpl is created without an error. The problem is just that batchSize is not set (has the default value 0).Michael Koch– Michael Koch2017年08月02日 14:18:49 +00:00Commented Aug 2, 2017 at 14:18
-
Did you try to inject the value via constructor?fateddy– fateddy2017年08月02日 15:15:24 +00:00Commented Aug 2, 2017 at 15:15
2 Answers 2
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.
Comments
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);
Comments
Explore related questions
See similar questions with these tags.