how to call the function that was using mysql keywords as parameters by the criteria query?

纵然是瞬间 提交于 2021-02-11 14:24:00

问题


I am using mysql with jpa specification query. I want to know how can i call the function that was using mysql keywords as parameters.

Here is the example:

select * from schema3.countries order by convert(name using GBK);

The convert method using the using and GBK keywords as paramters.

I want to call the convert function by the criteria query.

I tried the below but it does not working for me.

Expression expression = join.get(Country_.NAME);
                Expression orderExpression = builder.function(
                        "convert",
                        String.class,
                        expression,
                        builder.literal("USING GBK")
                );

and

Path path = join.get(Country_.NAME);

                String countryNameAlias = path.getAlias();
                Expression orderExpression = builder.function(
                        "convert",
                        String.class,
                        builder.literal(countryNameAlias + " USING GBK")
                );

The variable countryNameAlias is null so it`s not working.

Here is the error :

Hibernate: select expert0_.id as id1_14_, expert0_.code as code2_14_, expert0_.created_at as created_3_14_, expert0_.expert_information as expert_i4_14_, expert0_.meta_data_of_the_expert_information as meta_dat5_14_, expert0_.motherland as motherla8_14_, expert0_.number_of_applications as number_o6_14_, expert0_.updated_at as updated_7_14_, JSON_EXTRACT(expert0_.expert_information, '$.basicInformation.birthDate') as formula4_, case
           when
               JSON_EXTRACT(expert0_.expert_information, '$.basicInformation.gender') = 'MALE'
            then 0
else 1 end as formula5_, JSON_EXTRACT(expert0_.expert_information, '$.basicInformation.nameEN') as formula6_, convert(JSON_EXTRACT(expert0_.expert_information, '$.basicInformation.nameZH') using GBK) as formula7_ from expert expert0_ left outer join expert_application_record expertappl1_ on expert0_.id=expertappl1_.expert_id left outer join countries country2_ on expert0_.motherland=country2_.id where expertappl1_.latest=? order by convert(?) desc limit ?
2019-11-05 18:58:41.281 TRACE 15252 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [BOOLEAN] - [true]
2019-11-05 18:58:41.281 TRACE 15252 --- [nio-8080-exec-2] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [VARCHAR] - [null USING GBK]
2019-11-05 18:58:41.282  WARN 15252 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1064, SQLState: 42000
2019-11-05 18:58:41.282 ERROR 15252 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ') desc limit 10' at line 5
2019-11-05 18:58:41.285 ERROR 15252 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet] with root cause

java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ') desc limit 10' at line 5
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:118) ~[mysql-connector-java-8.0.11.jar:8.0.11]
    at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95) ~[mysql-connector-java-8.0.11.jar:8.0.11]
    at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-java-8.0.11.jar:8.0.11]
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:960) ~[mysql-connector-java-8.0.11.jar:8.0.11]
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeQuery(ClientPreparedStatement.java:1019) ~[mysql-connector-java-8.0.11.jar:8.0.11]
    at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52) ~[HikariCP-2.7.9.jar:na]

Thanks you all.


回答1:


The @formula anotation cannot work with native query. Here is the link: I am the link.

I am not familiar the jpa too much but here is the better solution than using the @Formula annotation.

First you need to create a custom Dialect Here is the example:

public class CustomMariaDB53Dialect extends MariaDB53Dialect {
    private static final Logger LOG = LoggerFactory.getLogger(CustomMariaDB53Dialect.class);

    public CustomMariaDB53Dialect() {
        super();
}

And then register a function to the Dialect Here is the code:

public class CustomMariaDB53Dialect extends MariaDB53Dialect {
    private static final Logger LOG = LoggerFactory.getLogger(CustomMariaDB53Dialect.class);

    public CustomMariaDB53Dialect() {
        super();
        registerFunction("convertEncode", new SQLFunctionTemplate(StandardBasicTypes.STRING, "convert(?1 using ?2)"));
    }
}

The first param of the registerFunction method is function name.

The second is a implement of SqlFunction

The ?1 and ?2 means the args of the function So the function template is convert(?1 using ?2)

After above stpes done.

You should tell the hibernate you are using a new Dialect

The path of the config is hibernate.dialect

Here is the example:

hibernate.dialect=xxx.xxx.CustomMariaDB53Dialect 

If you are using spring boot jpa.

Here is the config:

spring:
  jpa:
    hibernate:
    properties:
      hibernate:
        dialect: xxx.xxx.CustomMariaDB53Dialect

The last step is to use the function in your query.

Here is the example:

Expression<String> countryName = builder().function(
                        "convertEncode", 
                        String.class,
                        join.get(Country_.NAME),
                        builder().literal("gbk")
                );

                return direction.isDescending() ? builder().desc(countryName ) : builder().asc(countryName );

Here is the final sql:

order by convert(expert0_.name_zh using ?) asc limit ?

Cheers!!!




回答2:


I changed the sql to below :

select *, convert(name using GBK) as nameGBK from schema3.countries order by nameGBK;

So the next question is how to add computed column to the selection. We can add a column to the country model. The field name is nameGBK and use the @Formula annotation; here is the code

    @Column(nullable = false, unique = true)
    private String name;

    @Formula("convert(name using GBK)")
    private String nameGBK;

and then in the criteria query we can order by the nameGBK. Here is the code:

builder.desc(join.get(Country_.NAME_GBK));

But the jpa I am using cannot parse the keywords using and GBK so we must extend the Dialect of which you are using and register the keywords.

Here is the code:

public class CustomMariaDB53Dialect extends MariaDB53Dialect {
    private static final Logger LOG = LoggerFactory.getLogger(CustomMariaDB53Dialect.class);

    public CustomMariaDB53Dialect() {
        super();
        registerKeyword("using");
        registerKeyword("USING");
        registerKeyword("GBK");
        registerKeyword("gbk");
    }
}

And change the config to tell the hibernate you are using a new Dialect. If you are using spring data jpa. Then here is the config:

spring:
  h2:
    console:
      enabled: true
  datasource:
    url: jdbc:mysql://dev:13306/schema3?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8&zeroDateTimeBehavior=CONVERT_TO_NULL&nullCatalogMeansCurrent=true
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    initialization-mode: always
  liquibase:
    enabled: false
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        order_inserts: true
        #here to tell the hibernate you are using a new dialect.
        dialect: com.hide.hide.CustomMariaDB53Dialect
        jdbc:
          batch_size: 100

If this helped you give me a vote please. Thank you.



来源:https://stackoverflow.com/questions/58709930/how-to-call-the-function-that-was-using-mysql-keywords-as-parameters-by-the-crit

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!