Ebean 字段别名配置引发语法问题

  • Post author:
  • Post category:其他




前言

Ebean 没听过?这里简单给你介绍下:Ebean 是一个 Java ORM(Object-Relational Mapping)框架,具有以下优势:

  1. 简单易用:Ebean 的 API 设计简单易用,可以快速上手,无需繁琐的配置。

  2. 自动化 SQL 生成:Ebean 可以自动化生成 SQL 语句,开发者无需手写 SQL,提高了开发效率。

  3. 高性能:Ebean 的性能优秀,支持缓存、预编译等优化手段,可以快速处理大量数据。

  4. 多数据源支持:Ebean 支持多数据源,可以同时连接多个数据库,方便应用在不同环境下的部署和迁移。

  5. 事务管理:Ebean 提供了完整的事务管理机制,保证数据的一致性和可靠性。

  6. 灵活的查询:Ebean 支持灵活的查询方式,可以根据需求灵活组合条件、排序和分页等操作。

简单来说,Ebean 是一个功能丰富、易于使用且性能优越的 ORM 框架,适用于各种规模的 Java 项目,尤其是丰富的流式 API 封装,使用很丝滑,举个例子:

// "alias" bean that can be used in select and fetch clauses
QCustomer cust = QCustomer.alias();

List<Customer> customers =
  new QCustomer()
    // only fetch some properties of customer (partial objects)
    .select(cust.name, cust.version, cust.whenCreated)
    .name.istartsWith("Rob")
    .findList();



项目场景:

最近接手一个项目,持久层就使用了 Ebean 框架。刚接手的项目,你懂的,总有一些坑需要你去踩!

需求很简答:要对查询结果字段做去重并统计总数,很简单的问题,直接翻译成代码即可:

        int count = queryBean()
                .select(QCallRecord.alias().contentId)
                .type.eq("user")
                .setDistinct(true)
                .findCount();

正常情况下,这样使用就 OK 了




问题描述

问题来了:

Caused by: 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 'c0) from call_record t0 where t0.type = 'user'' at line 1

也就是语法有问题,我们看看生成的 SQL 语法:

select count(distinct t0.content_id c0) from call_record t0 where t0.type = 'user'

问题在哪? 字段 content_id 的别名。

在 MySQL 中,对于 count 这类聚合函数内是不允许出现字段别名,别名 c0 是在函数参数中定义的,这是一种错误的写法,会导致语法错误。

正确的写法应该是将 AS 关键字放在函数后面,比如:

SELECT count(DISTINCT t0.content_id) AS c0 FROM call_record t0 WHERE t0.type = 'user';

更深层原因?

SQL 聚合函数是作用于某个列或表达式的数据集上,并计算这些值的总和、平均值、最小值、最大值等等。因此,在 SQL 查询中,聚合函数的参数应该是列或表达式,而不是别名。




原因分析:

Ebean 怎么会解析出有语法问题的 SQL?是bug 还是配置问题?

Ebean 提供了 DatabasePlatform 这么一个数据库平台抽象类,它定义了一系列通用的接口和方法,以便与不同的数据库进行交互,具体差异由子类去个性化实现,你可以看看:

在这里插入图片描述

有多少是自己熟悉的数据库?

不同的数据库难免有些差异,比如 MySQL、Postgres 等,当然 Ebean 已经把这些差异化都实现了,默认情况下自动选择,对使用者来说无感知!!! 除非你特性化需求配置。

当然,我就遇到了。DB 自定义配置项:

    @Override
    public Database getObject() throws Exception {
        DatabaseConfig databaseConfig = new DatabaseConfig();
        
        ...
        
        DatabasePlatform dbPlatform = new DatabasePlatform();
        dbPlatform.getDbIdentity().setIdType(IdType.IDENTITY);
        dbPlatform.getDbIdentity().setSupportsGetGeneratedKeys(true);
        dbPlatform.getDbIdentity().setSupportsSequence(false);
        dbPlatform.getDbIdentity().setSupportsIdentity(true);
        databaseConfig.setDatabasePlatform(dbPlatform);
 
        ...

        return DatabaseFactory.create(databaseConfig);
    }

项目里手动配置这几个参数,但是配置不够完整,部分适用于 MySQL 的默认配置没有加上,比如我们这缺失的 :

        dbPlatform.setColumnAliasPrefix(null);

不过查看 Ebean 对于 MySQL 的默认配置:

在这里插入图片描述

你看,默认的参数和手动配置参数值的是否一样?同时也更加完善,大部分时候,默认配置可能都是最优解。




解决方案:

新增配置项:

  dbPlatform.setColumnAliasPrefix(null);

取消这种默认定义别名的方式,最后的正确的 SQL 语法:

select count(distinct t0.content_id) from call_record t0 where t0.type = 'user';

当然,也不排除这个本身是 Ebean 的问题,可以进一步做的更好,比如改进聚合函数取别名的方式等。

不同框架在底层细节实现上可能有些不同,但一般都留足了可扩展的口子,提供给你去定制化使用;当然,如非必要,直接使用默认配置可能是最优解!



版权声明:本文为ldw201510803006原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。