Spring Data Solr @Transaction提交

时间:2016-11-09 18:40:02

标签: spring solr

我目前有一个设置,其中数据被插入到数据库中,并被索引到Solr中。这两个步骤通过@Transaction批注包装在Spring管理的事务中。我注意到的是,每当事务关闭时,spring-data-solr都会使用以下参数发出更新:params {commit = true& softCommit = false& waitSearcher = true}

@Transactional
public void save(Object toSave){
    dbRepository.save(toSave);
    solrRepository.save(toSave);
}

提交到solr的速率相当高,所以理想情况下我喜欢将数据发送到solr索引,并且定期进行solr自动提交。我在solrconfig.xml中设置了autoCommit(和autoSoftCommit),但由于spring-data-solr正在发送这些提交参数,因此每次都会执行一次硬提交。

我知道我可以下载到SolrTemplate API并手动发出提交,如果可能的话,我想在弹簧管理的事务中保持solr repository.save调用。有没有办法修改在提交时发送给solr的参数?

3 个答案:

答案 0 :(得分:0)

在org.springframework.data.solr.repository.support.SimpleSolrRepository中输入IDE调试断点后:

private void commitIfTransactionSynchronisationIsInactive() {
    if (!TransactionSynchronizationManager.isSynchronizationActive()) {
        this.solrOperations.commit(solrCollectionName);
    }
}

我发现将我的代码包装为@Transactional(以及实际启用框架以开始/结束代码作为事务的其他细节)并没有实现我们期望的#SAPs for Spring Solr" Spring Data for Apache Solr&#34 34 ;.堆栈跟踪显示代码的事务范围的代理和事务拦截器类,但随后它还显示框架使用自己的另一个代理和事务拦截器启动自己的嵌套事务。当框架退出我的代码调用的CrudRepository.save()方法时,提交给Solr的操作是由框架的嵌套事务完成的。它发生在我们的外部交易退出之前。因此,尝试批量处理许多保存,最后一次提交而不是每次保存一次提交是徒劳的。看来,对于我的代码中的这个区域,我必须使用SolrJ将我的实体保存(更新)到Solr,然后让我的"我的"事务的退出跟随提交。

答案 1 :(得分:0)

如果使用 Spring Solr,我发现使用 SolrTemplate bean 允许您在向 Solr 索引添加数据时“批量”更新。通过将 bean 用于 SolrTemplate,您可以使用“addBeans”方法,该方法将向索引添加一个集合,并且在事务结束之前不会提交。在我的例子中,我开始使用 solrClient.add() 并花费 4 个小时让我的集合通过迭代保存到索引中,因为它在每次保存后提交。通过使用 solrTemplate.addBeans(Collect),它在 1 秒内完成,因为提交是在整个集合上。这是一个代码片段:

@Resource
SolrTemplate solrTemplate;

public void doReindexing(List<Image> images) {

    if (images != null) {

        /* CMSSolrImage is a class with @SolrDocument mappings.
         * the List<Image> images is a collection pulled from my database
         * I want indexed in Solr. 
         */
        List<CMSSolrImage> sImages = new ArrayList<CMSSolrImage>();
        
        for (Image image : images) {
            
            CMSSolrImage sImage = new CMSSolrImage(image);
            
            sImages.add(sImage);
            
        }
        
        solrTemplate.saveBeans(sImages);

    }

}

答案 2 :(得分:-2)

我做类似事情的方法是创建保存方法的自定义存储库实现。

存储库的接口:

public interface FooRepository extends SolrCrudRepository<Foo, String>, FooRepositoryCustom {
}

自定义覆盖的界面:

public interface FooRepositoryCustom  {
    public Foo save(Foo entity);
    public Iterable<Foo> save(Iterable<Foo> entities);
}

自定义覆盖的实现:

public class FooRepositoryImpl  {

    private SolrOperations solrOperations;

    public SolrSampleRepositoryImpl(SolrOperations fooSolrOperations) {
        this.solrOperations = fooSolrOperations;
    }

    @Override
    public Foo save(Foo entity) {
        Assert.notNull(entity, "Cannot save 'null' entity.");
        registerTransactionSynchronisationIfSynchronisationActive();
        this.solrOperations.saveBean(entity, 1000);
        commitIfTransactionSynchronisationIsInactive();
        return entity;
    }

    @Override
    public Iterable<Foo> save(Iterable<Foo> entities) {
        Assert.notNull(entities, "Cannot insert 'null' as a List.");

        if (!(entities instanceof Collection<?>)) {
            throw new InvalidDataAccessApiUsageException("Entities have to be inside a collection");
        }

        registerTransactionSynchronisationIfSynchronisationActive();
        this.solrOperations.saveBeans((Collection<? extends T>) entities, 1000);
        commitIfTransactionSynchronisationIsInactive();
        return entities;
    }

    private void registerTransactionSynchronisationIfSynchronisationActive() {
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            registerTransactionSynchronisationAdapter();
        }
    }

    private void registerTransactionSynchronisationAdapter() {
        TransactionSynchronizationManager.registerSynchronization(SolrTransactionSynchronizationAdapterBuilder
                .forOperations(this.solrOperations).withDefaultBehaviour());
    }

    private void commitIfTransactionSynchronisationIsInactive() {
        if (!TransactionSynchronizationManager.isSynchronizationActive()) {
            this.solrOperations.commit();
        }
    }
}

您还需要为正确的solr核心提供SolrOperations bean:

@Configuration
public class FooSolrConfig {

    @Bean
    public SolrOperations getFooSolrOperations(SolrClient solrClient) {
        return new SolrTemplate(solrClient, "foo");
    }
}

脚注:自动提交(在我看来)在概念上与事务不兼容。自动提交是solr的一个承诺,它将尝试在一定的时间限制内开始将其写入磁盘。然而,许多事情可能会阻止实际发生 - 及时的电源或硬件故障,文档和架构之间的错误等等。但是客户端不会知道solr未能履行其承诺,并且事务将在它确实失败了。