Spring data結(jié)合QueryDsl查詢Mongo的customize方法未加載的一個(gè)坑

最近做的產(chǎn)品基于spring boot, mongo進(jìn)行開發(fā)紊服,由于前端需要進(jìn)行比較復(fù)雜的查詢,因此引入了dsl相關(guān)包乳愉,版本信息如下:

com.querydsl:querydsl-mongodb:jar:4.1.4,
org.springframework.boot:spring-boot-devtools:jar:1.4.1.RELEASE,
 org.springframework:spring-context-support:jar:4.3.3.RELEASE

并定義了dsl相關(guān)的接口送挑,如下所示:

public interface VipRepository extends CrudRepository<Vip,   String>, QueryDslPredicateExecutor<Vip>,       QuerydslBinderCustomizer<Vip>
            , MongoRepository<Vip, String>{
  Override
    default public void customize(QuerydslBindings bindings,  Vip root) {
        log.debug ("[VipRepository]from customize");
        bindings.bind (String.class)
                .first ((StringPath path, String value) -> path.containsIgnoreCase (value));
    }
}

Resource中注入該dao:

@RestController
@RequestMapping("/v1/vip")
public class VipResource {

    private final Logger log = LoggerFactory.getLogger (VipResource.class);

    @Inject
    private VipRepository vipRepository;

@RequestMapping(value = "/vip/list", method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
    @Timed
    @Secured(AuthoritiesConstants.ADMIN)
    public ResponseEntity<List<Vip>> listVip(@QuerydslPredicate(root =
            Vip.class) Predicate predicate,
                                                               Pageable pageable) throws
            URISyntaxException {
        log.debug ("[viplist]");
        Page<Vip> page = vipRepository.findAll (predicate, pageable);
        log.debug ("[listSimMonthGprs] page total elements: {}", page.getTotalElements ());
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders (page,
                "/v1/vip/list");
        return new ResponseEntity<> (results, headers, HttpStatus.OK);
    }
...

但在測試中發(fā)現(xiàn),在調(diào)用/vip/list時(shí)哈误,

有時(shí)會(huì)調(diào)用VipRepository中的customize方法哩至,有時(shí)卻不會(huì)。

實(shí)在奇怪黑滴。嘗試在VipRepository添加日志憨募,或者加斷點(diǎn)調(diào)試,添加serializable接口都是一樣的效果袁辈,只有spring context啟動(dòng)后第一次調(diào)用該接口加載不上菜谣,以后也都加載不上。
于是嘗試從spring data,querydsl的源碼進(jìn)行調(diào)試尾膊。在未深入研究spring data和querydsl源碼的情況下如何加斷點(diǎn)呢媳危?觀察到在customize方法的參數(shù)中引入有QuerydslBindings這個(gè)綁定接口,結(jié)合之前對(duì)querydsl的研究冈敛,該接口完成domain類和Q類之前的參數(shù)綁定待笑,于是利用IDE的功能找到該接口的實(shí)現(xiàn)類QuerydslBindingsFactory,該類源碼如下:

public class QuerydslBindingsFactory implements ApplicationContextAware {
    private final EntityPathResolver entityPathResolver;
    private final Map<TypeInformation<?>, EntityPath<?>> entityPaths;
    private AutowireCapableBeanFactory beanFactory;
//該類cache了系統(tǒng)中所有的domain類與repository名稱的鍵值對(duì)map
    private Repositories repositories;
    public QuerydslBindings createBindingsFor(Class<? extends QuerydslBinderCustomizer<?>> customizer,
            TypeInformation<?> domainType) {

        Assert.notNull(domainType, "Domain type must not be null!");

        EntityPath<?> path = verifyEntityPathPresent(domainType);

        QuerydslBindings bindings = new QuerydslBindings();
        findCustomizerForDomainType(customizer, domainType.getType()).customize(bindings, path);

        return bindings;
    }

    /**
     * Tries to detect a Querydsl query type for the given domain type candidate via the configured
     * {@link EntityPathResolver}.
     * 
     * @param candidate must not be {@literal null}.
     * @throws IllegalStateException to indicate the query type can't be found and manual configuration is necessary.
     */
    private EntityPath<?> verifyEntityPathPresent(TypeInformation<?> candidate) {

        EntityPath<?> path = entityPaths.get(candidate);

        if (path != null) {
            return path;
        }

        Class<?> type = candidate.getType();

        try {
            path = entityPathResolver.createPath(type);
        } catch (IllegalArgumentException o_O) {
            throw new IllegalStateException(
                    String.format(INVALID_DOMAIN_TYPE, candidate.getType(), QuerydslPredicate.class.getSimpleName()), o_O);
        }

        entityPaths.put(candidate, path);
        return path;
    }

    /**
     * Obtains the {@link QuerydslBinderCustomizer} for the given domain type. Will inspect the given annotation for a
     * dedicatedly configured one or consider the domain types's repository.
     * 
     * @param annotation
     * @param domainType
     * @return
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private QuerydslBinderCustomizer<EntityPath<?>> findCustomizerForDomainType(
            Class<? extends QuerydslBinderCustomizer> customizer, Class<?> domainType) {

        if (customizer != null && !QuerydslBinderCustomizer.class.equals(customizer)) {
            return createQuerydslBinderCustomizer(customizer);
        }

        if (repositories != null && repositories.hasRepositoryFor(domainType)) {

            Object repository = repositories.getRepositoryFor(domainType);

            if (repository instanceof QuerydslBinderCustomizer) {
                return (QuerydslBinderCustomizer<EntityPath<?>>) repository;
            }
        }

        return NoOpCustomizer.INSTANCE;
    }

    /**
     * Obtains a {@link QuerydslBinderCustomizer} for the given type. Will try to obtain a bean from the
     * {@link org.springframework.beans.factory.BeanFactory} first or fall back to create a fresh instance through the
     * {@link org.springframework.beans.factory.BeanFactory} or finally falling back to a plain instantiation if no
     * {@link org.springframework.beans.factory.BeanFactory} is present.
     * 
     * @param type must not be {@literal null}.
     * @return
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private QuerydslBinderCustomizer<EntityPath<?>> createQuerydslBinderCustomizer(
            Class<? extends QuerydslBinderCustomizer> type) {

        if (beanFactory == null) {
            return BeanUtils.instantiateClass(type);
        }

        try {
            return beanFactory.getBean(type);
        } catch (NoSuchBeanDefinitionException e) {
            return beanFactory.createBean(type);
        }
    }
/**
*
**/
    private static enum NoOpCustomizer implements QuerydslBinderCustomizer<EntityPath<?>> {
        INSTANCE;
        @Override
        public void customize(QuerydslBindings bindings, EntityPath<?> root) {}
    }
}

This class will be invoked before entering resource method. It is charge of generating predicate instance from request parameters.
At first, calling createBindingsFor method. In the method, invoking verifyEntityPathPresent to get path. Then call findCustomizerForDomainType to check if dsl repository exists.
Pay attention to this sentence:

Object repository = repositories.getRepositoryFor(domainType);

This sentence gets repository object according to domainType. When using Vip to query, I found it return another repository for Vip not DSL repository.

到這個(gè)地方抓谴,大家就比較清楚原因了:因?yàn)橄到y(tǒng)中針對(duì)同一個(gè)domain定了兩個(gè)repository暮蹂,而spring在加載時(shí)使用domain class作為key,repository name作為值癌压,只能隨機(jī)cache一個(gè)repository仰泻,這也是有時(shí)可以調(diào)用到customize,而有時(shí)不可以的原因滩届。

找到原因后集侯,該問題就很好解決了,把兩個(gè)repository接口融合到一起即可帜消。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末棠枉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子泡挺,更是在濱河造成了極大的恐慌辈讶,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粘衬,死亡現(xiàn)場離奇詭異荞估,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)稚新,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門勘伺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人褂删,你說我怎么就攤上這事飞醉。” “怎么了屯阀?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵缅帘,是天一觀的道長。 經(jīng)常有香客問我难衰,道長钦无,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任盖袭,我火速辦了婚禮失暂,結(jié)果婚禮上彼宠,老公的妹妹穿的比我還像新娘。我一直安慰自己弟塞,他們只是感情好凭峡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著决记,像睡著了一般摧冀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上系宫,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天索昂,我揣著相機(jī)與錄音,去河邊找鬼笙瑟。 笑死楼镐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的往枷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼凄杯,長吁一口氣:“原來是場噩夢啊……” “哼错洁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起戒突,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后薯蝎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體禁灼,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年隔崎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了今艺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡爵卒,死狀恐怖虚缎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钓株,我是刑警寧澤实牡,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站轴合,受9級(jí)特大地震影響创坞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜受葛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一题涨、第九天 我趴在偏房一處隱蔽的房頂上張望偎谁。 院中可真熱鬧,春花似錦携栋、人聲如沸搭盾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸯隅。三九已至,卻和暖如春向挖,著一層夾襖步出監(jiān)牢的瞬間蝌以,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國打工何之, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跟畅,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓溶推,卻偏偏與公主長得像徊件,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蒜危,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理虱痕,服務(wù)發(fā)現(xiàn),斷路器辐赞,智...
    卡卡羅2017閱讀 134,654評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,808評(píng)論 6 342
  • 文章作者:Tyan博客:noahsnail.com 2.Introduction to the Spring Fr...
    SnailTyan閱讀 5,390評(píng)論 7 56
  • 3.1. 核心概念 CrudRepository包含增刪查改基礎(chǔ)功能 PagingAndSortingReposi...
    titvax閱讀 1,755評(píng)論 0 2
  • 他是我在高中時(shí)結(jié)識(shí)的一個(gè)人部翘,那時(shí)候我高一,他留級(jí)到我們班响委,一開始很不合新思,還打過一架,后來因?yàn)橛行┦伦屛覀兂蔀榱伺栌?..
    MC灬淺墨閱讀 151評(píng)論 0 1