MySQL报错:MySQL server version for the right syntax to use near ‘nulls last‘ at line 1

文章讲述了在使用HibernateORM框架时,配置`order_by.default_null_ordering:last`在MySQL数据库中导致的SQL语法错误。作者分析了问题原因(PostgreSQL方言特性在MySQL中的不兼容),给出了在MySQL中正确处理空值排序的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

配置order_by.default_null_ordering: last后报错: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 ‘nulls last’ at line 1
2023-07-31 17:32:15.460 [http-nio-8180-exec-1] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper.logExceptions(SqlExceptionHelper.java:142) - 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 ‘nulls last’ at line 1
在这里插入图片描述

记录下出这个问题的原因

我们公司用的ORM框架是Hibenrnate,数据库主要用的是Postgre SQL,也关联的有个MySQL。(涉及使用MySQL数据源查询的需求很少)
因为有些日期为空的特殊数据,同事在yml配置文件里加入了下面这个配置:

spring:
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQLDialect # 指定了使用的数据库方言,这里是PostgreSQL数据库的方言。
        order_by.default_null_ordering: last # last 是一个Hibernate属性,它指定了在排序时如何处理空值。在这种情况下,空值将被放在排序结果的最后。

因为涉及使用MySQL数据源查询的需求很少,上线的时候测试同事也没测到,上线两天后,运营那边反馈新建XXX报系统异常。我查看生产日志发现:

13:26:13 [http-nio-8180-exec-10] WARN  o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 1064, SQLState: 42000
13:26:13 [http-nio-8180-exec-10] ERROR o.h.e.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 'nulls last' at line 1
13:26:13 [http-nio-8180-exec-10] ERROR c.ccm.amarth.GlobalExceptionHandler - 运行时异常
org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
	at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:259)
	at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551)
	at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
	at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
	at com.sun.proxy.$Proxy250.findAll(Unknown Source)
	# 可以看到错误代码在这里(这里我改下包路径和方法名,涉及到源码担心泄露)
	at com.demo.test.service.UserServiceImpl.findFirstByUserIdOrderByDate(UserServiceImpl.java:117)

因为代码不是我写的,而且这段逻辑长时间都没有动过。起初也没往yml配置那里想,因为代码也不是我加的,当时我初步判断应该是数据库的问题,以为是数据库里这条记录有空日期,所以查询报错了。
发现数据没有问题后,我在本地配置了SQL打印日志:

logging.level.org.hibernate.SQL: DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder: TRACE

查看执行SQL日志:

13:26:13 [http-nio-8180-exec-10] DEBUG org.hibernate.SQL - select enterprise0_.id as id1_2_, enterprise0_.name as nam6_2_ from users enterprise0_ where enterprise0_.org_data_id=? order by enterprise0_.date desc nulls last
13:26:13 [http-nio-8180-exec-10] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - [45642005]

# 发现后尾巴 竟然拼接了个
order by enterprise0_.date desc nulls last

排查到是yml配置的问题后,我把这段代码注释掉后:

spring:
  jpa:
    open-in-view: true # 在view层打开数据库session
#     properties:
#       hibernate:
#         dialect: org.hibernate.dialect.PostgreSQLDialect # 指定了使用的数据库方言,这里是PostgreSQL数据库的方言。
#         order_by.default_null_ordering: last # last 是一个Hibernate属性,它指定了在排序时如何处理空值。在这种情况下,空值将被放在排序结果的最后。

发现程序可以正常进行了。

总结:

这个错误是由于在MySQL数据库中使用了不支持的语法导致的。具体来说,order_by.default_null_ordering: last 这个属性是针对PostgreSQL数据库的,用于指定在排序时如何处理空值。然而,MySQL数据库不支持这个语法。
我查了一下:MySQL 8.0版本及以上支持使用NULLS LAST语法来处理空值的排序。

因为是生产环境,公司不可能会因为这个bug而升级数据库版本,所以只能自行解决。

解决方案:

因为项目使用的是 JPA,所以涉及到查询的方法采用 Sort.Order.nullsLast()方法将空值放到排序结果的最后。
代码示例:

String sysUserId = sysUser.getId();
PageRequest page = PageRequest.of(dto.getPage(), dto.getSize());
Specification<CourseEntity> specification =  (root, criteriaQuery, criteriaBuilder) -> {
    var list = new ArrayList<Predicate>();
    if(StrUtil.isNotBlank(userId)){
        list.add(criteriaBuilder.equal(root.get("userId"),userId));
    }
    // 将createDate = null 的数据,设置个默认最小时间,然后倒叙排列
    Expression<LocalDateTime> beginTimeExpression = root.get("createDate");
    Expression<LocalDateTime> nullLiteral = criteriaBuilder.literal(LocalDateTime.of(1999, 1,1,23,59, 12));
    Order orderByBeginTime = criteriaBuilder.desc(criteriaBuilder.selectCase()
            .when(criteriaBuilder.isNull(beginTimeExpression), nullLiteral)
            .otherwise(beginTimeExpression));
    criteriaQuery.orderBy(orderByBeginTime);
    Predicate[] predicates = new Predicate[list.size()];
    return criteriaBuilder.and(list.toArray(predicates));
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值