data jpa扩展支持动态sql

薄兴昌
2023-12-01

data jpa扩展支持动态sql

更多干货

 

前言

一般在写业务接口的过程中,很有可能需要实现可以动态组合各种查询条件的接口。如果我们根据一种查询条件组合一个方法的做法来写,那么将会有大量方法存在,繁琐,维护起来相当困难。想要实现动态查询,其实就是要实现拼接SQL语句。

代码地址github

spring-data-jpa-extra

一、如何使用之定义动态sql在文件

  • sql 默认支持后缀sftl和xml

定义sql文件默认与实体类名字相同如:Sample.sftl

使用sftl定义方式

-- findByContent
  SELECT * FROM t_sample WHERE 1 = 1
  <#if content??>
        AND content LIKE :content
  </#if>

--countContent
SELECT count(*) FROM t_sample WHERE 1 = 1
<#if content??>
  AND content LIKE :content
</#if>

--findDtos
SELECT id, content as contentShow FROM t_sample

--findByTemplateQueryObject
SELECT * FROM t_sample WHERE 1 = 1
<#if content??>
 AND content LIKE :content
</#if>

--findMap
SELECT * FROM t_sample

xml方式定义

<?xml version="1.0" encoding="utf-8" ?>
<sqls xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns="http://www.slyak.com/schema/templatequery"
      xsi:schemaLocation="http://www.slyak.com/schema/templatequery http://www.slyak.com/schema/templatequery.xsd">

    <sql name="findByContent">
        <![CDATA[
          SELECT * FROM t_sample WHERE 1=1
          <#if content??>
            AND content LIKE :content
          </#if>
        ]]>
    </sql>
    <sql name="countContent">
        <![CDATA[
          SELECT count(*) FROM t_sample WHERE 1=1
          <#if content??>
            AND content LIKE :content
          </#if>
        ]]>
    </sql>
    <sql name="findDtos">
        <![CDATA[
          SELECT id,content as contentShow FROM t_sample
        ]]>
    </sql>
    <sql name="findByTemplateQueryObject">
        <![CDATA[
          SELECT * FROM t_sample WHERE 1=1
          <#if content??>
            AND content LIKE :content
          </#if>
        ]]>
    </sql>
</sqls>

二、如何使用之 SampleRepository

  • @TemplateQuery 自定义注解:repository 中的方法如果有注解TemplateQuery,则会根据方法名字到对于的文件中查找对于的sql
public interface SampleRepository extends GenericJpaRepository<Sample, Long> {

	@TemplateQuery
	Page<Sample> findByContent(String content, Pageable pageable);

	@TemplateQuery
	List<Sample> findByTemplateQueryObject(SampleQuery sampleQuery, Pageable pageable);

	@TemplateQuery
	Long countContent(String content);

	@TemplateQuery
	List<SampleDTO> findDtos();

	// #{name?:'and content like :name'}
	@Query(nativeQuery = true, value = "select * from t_sample where content like ?1")
	List<Sample> findDtos2(String name);

	@TemplateQuery
	List<Map<String,Object>> findMap();
}

源代码解析

一、自定义一注解用于区分哪些方法需要使用到动态sql

TemplateQuery.java

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
@QueryAnnotation
@Documented
public @interface TemplateQuery {
	String value() default "";
}

二、GenericJpaRepositoryFactory.java

  • GenericJpaRepositoryFactory 中重写getQueryLookupStrategy(动态创建查询方法)
@Override
	protected QueryLookupStrategy getQueryLookupStrategy(QueryLookupStrategy.Key key,
			EvaluationContextProvider evaluationContextProvider) {
		return TemplateQueryLookupStrategy.create(entityManager, key, extractor, evaluationContextProvider);
	}

三、TemplateQueryLookupStrategy.java

TemplateQueryLookupStrategy 模版查询策略 其中关键代码:

@Override
	public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
			NamedQueries namedQueries) {
		if (method.getAnnotation(TemplateQuery.class) == null) {
			return jpaQueryLookupStrategy.resolveQuery(method, metadata, factory, namedQueries);
		}
		else {
			return new FreemarkerTemplateQuery(new JpaQueryMethod(method, metadata, factory, extractor), entityManager);
		}
	}
  • resolveQuery 中解析query。判断方法上是否有注解TemplateQuery。如果有则使用动态sql。如果没有则使用jpa默认

四、FreemarkerTemplateQuery

  • FreemarkerTemplateQuery 结合freemarker动态构造Query对象
  • 关键代码如下
@Override
    protected TypedQuery<Long> doCreateCountQuery(Object[] values) {
        TypedQuery query = (TypedQuery) getEntityManager()
                .createNativeQuery(QueryBuilder.toCountQuery(getQuery(values)));
        bind(query, values);
        return query;
    }

关键类解析

  • FreemarkerSqlTemplates 从文件中解析sql。使用freemarker
  • SftlNamedTemplateResolver 解析.sftl文件 其中的sql 使用分隔符-- 隔开
  • QueryBuilder.toCountQuery 根据select sql 构造出 count sql 查询总条目
 类似资料: