1

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?

asked Aug 2, 2017 at 13:42
6
  • how do you set the "spring.jpa.properties.hibernate.jdbc.batch_size" value? Commented 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. Commented Aug 2, 2017 at 13:47
  • what is the error you get? Commented 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). Commented Aug 2, 2017 at 14:18
  • Did you try to inject the value via constructor? Commented Aug 2, 2017 at 15:15

2 Answers 2

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.

answered Oct 26, 2017 at 21:16
Sign up to request clarification or add additional context in comments.

Comments

0

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);
answered Apr 23, 2018 at 12:33

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.