package fr.openwide.core.jpa.migration.service; import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.List; import java.util.Map; import org.hibernate.PropertyValueException; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import fr.openwide.core.jpa.batch.executor.BatchExecutorCreator; import fr.openwide.core.jpa.batch.executor.MultithreadedBatchExecutor; import fr.openwide.core.jpa.batch.monitor.ProcessorMonitorContext; import fr.openwide.core.jpa.batch.runnable.ReadWriteBatchRunnable; import fr.openwide.core.jpa.business.generic.model.GenericEntity; import fr.openwide.core.jpa.business.generic.service.IGenericEntityService; import fr.openwide.core.jpa.exception.SecurityServiceException; import fr.openwide.core.jpa.exception.ServiceException; import fr.openwide.core.jpa.migration.rowmapper.AbstractListResultRowMapper; import fr.openwide.core.jpa.migration.rowmapper.AbstractMapResultRowMapper; import fr.openwide.core.jpa.migration.rowmapper.AbstractResultRowMapper; import fr.openwide.core.jpa.migration.util.IBatchEntityMigrationInformation; public abstract class AbstractBatchEntityMigrationService<T extends GenericEntity<Long, T>> extends AbstractMigrationService { @Autowired private BatchExecutorCreator batchCreator; public void importAllEntities() { List<Long> entityIds = ImmutableList.copyOf(getJdbcTemplate().queryForList(getMigrationInformation().getSqlAllIds(), Long.class)); MultithreadedBatchExecutor executor = batchCreator.newMultithreadedBatchExecutor(); executor.threads(4).batchSize(100); executor.run(getMigrationInformation().getEntityClass().getSimpleName(), entityIds, new ReadWriteBatchRunnable<Long>() { @Override public void executePartition(List<Long> partition) { importBatch(partition); } @Override public void postExecute() { updateSequence(getMigrationInformation().getEntityClass()); } }); } private void importBatch(List<Long> entityIds) throws PropertyValueException { List<T> entitiesList = Lists.newArrayList(); Map<Long, T> entitiesMap = Maps.newHashMapWithExpectedSize(entityIds.size()); preload(entityIds, getMigrationInformation()); try { MapSqlParameterSource entityIdsParameterSource = new MapSqlParameterSource(); entityIdsParameterSource.addValue(getMigrationInformation().getParameterIds(), entityIds); AutowireCapableBeanFactory autowire = applicationContext.getAutowireCapableBeanFactory(); RowMapper<?> rowMapper; Class<? extends AbstractResultRowMapper<?>> rowMapperClass = getMigrationInformation().getRowMapperClass(); if (AbstractMapResultRowMapper.class.isAssignableFrom(rowMapperClass)) { rowMapper = rowMapperClass.getConstructor(Map.class).newInstance(entitiesMap); } else if (AbstractListResultRowMapper.class.isAssignableFrom(rowMapperClass)) { rowMapper = rowMapperClass.getConstructor(List.class).newInstance(entitiesList); } else { throw new IllegalStateException(String.format("Type de rowmapper non reconnu %1$s", rowMapperClass.getSimpleName())); } autowire.autowireBean(rowMapper); autowire.initializeBean(rowMapper, rowMapper.getClass().getSimpleName()); prepareRowMapper(rowMapper, entityIds); getNamedParameterJdbcTemplate().query(getMigrationInformation().getSqlRequest(), entityIdsParameterSource, rowMapper); Collection<T> entities; if (AbstractMapResultRowMapper.class.isAssignableFrom(rowMapperClass)) { entities = entitiesMap.values(); } else if (AbstractListResultRowMapper.class.isAssignableFrom(rowMapperClass)) { entities = entitiesList; } else { throw new IllegalStateException(String.format("Type de rowmapper non reconnu %1$s", rowMapperClass.getSimpleName())); } for (T entity : entities) { if (getEntityService() != null) { getEntityService().create(entity); } else { entityManagerUtils.getEntityManager().persist(entity); } } } catch (RuntimeException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ServiceException | SecurityServiceException e) { getLogger().error("Erreur lors de la persistence d'un paquet de {}. {} créations annulées.", getMigrationInformation().getEntityClass().getSimpleName(), entityIds.size(), e); ProcessorMonitorContext.get().getDoneItems().addAndGet(-1 * entityIds.size()); } } protected void prepareRowMapper(RowMapper<?> rowMapper, List<Long> entityIds) { } protected abstract IBatchEntityMigrationInformation<T> getMigrationInformation(); protected abstract Logger getLogger(); /** * Override this if you want to use a GenericEntityService when creating entities. * <p><strong>Note:</strong>This is not recommended, since these services generally assume that they are really * creating a brand new row, not importing it. They may, for instance, set the "creationdate" attribute of an * entity to the current time, which is probably wrong when migrating. */ protected IGenericEntityService<Long, T> getEntityService() { return null; } }