您现在的位置是:首页 >学无止境 >Ebean 字段别名配置引发语法问题网站首页学无止境
Ebean 字段别名配置引发语法问题
前言
Ebean 没听过?这里简单给你介绍下:Ebean 是一个 Java ORM(Object-Relational Mapping)框架,具有以下优势:
-
简单易用:Ebean 的 API 设计简单易用,可以快速上手,无需繁琐的配置。
-
自动化 SQL 生成:Ebean 可以自动化生成 SQL 语句,开发者无需手写 SQL,提高了开发效率。
-
高性能:Ebean 的性能优秀,支持缓存、预编译等优化手段,可以快速处理大量数据。
-
多数据源支持:Ebean 支持多数据源,可以同时连接多个数据库,方便应用在不同环境下的部署和迁移。
-
事务管理:Ebean 提供了完整的事务管理机制,保证数据的一致性和可靠性。
-
灵活的查询: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 的问题,可以进一步做的更好,比如改进聚合函数取别名的方式等。
不同框架在底层细节实现上可能有些不同,但一般都留足了可扩展的口子,提供给你去定制化使用;当然,如非必要,直接使用默认配置可能是最优解!