Skip to content

查询字段(selection)

SELECT 语句的 select子句 支持传入多种类型当作 selection

表达式/函数/列/表等

支持org.huiche.ast.expression.Expression 及其所有子类,Expression带有多种内置支持的SQL语法支持,可以进行简单的SQL运算或操作

java
Column col = Column.of("version");
Sql.select(col,col.plus(1),Sql.min(col))
Sql.select(Sql.case_(col).when(1).then(10).when(2).then(20).end())

org.huiche.ast.support.Sql为内置sql函数和常用语句的包装工具类,均为static方法,可以用其创建语句,调用函数等,可以import static后直接调用

java
import static org.huiche.ast.support.Sql.*;

// 之后不用再写"Sql."
Column col = Column.of("version");
select(col,col.plus(1),min(col))
select(case_(col).when(1).then(10).when(2).then(20).end())

别名对象

org.huiche.ast.expression.AliasSegment,所有表达式可以定义别名as("xxx")后变为AliasSegment对象

表对象

包括org.huiche.ast.metadata.TableRef及其所有子类,自动转换为table.*,选择其所有列

huiche-apt生成的Q文件中的对象

其本质是Table的子类和Column的子类

了解huiche-apt

标量子查询

包括org.huiche.ast.statement.Select Select语句 和 org.huiche.ast.expression.SubQueryExpression 子查询表达式

需要自行确保其为标量子查询(一行一列)

说明

  • Select语句执行.asSubQuery()即转换为SubQueryExpression对象,无别名子查询
  • Select语句执行.as(alias)会转换为DerivedTableRef对象,是带有别名的子查询(派生表),无法当作selection

实体类Class

当作表,转换为table.*

排除实体类某些字段

Sql.execludeColumn 可能会有所帮助,建议如果实体类属性比较多,需要排除其中几个属性时使用

java
record User(Long id,String name,String password){}
Sql.select(Sql.execludeColumn(User.class,User::password));
// 实际会select User::id,User::name,而忽略User::password

lambda方法引用

当作列,Entity::field转换为table.column

注意, 如果实体类存在父级对象,即某些属性定义在父类中, 那父类属性无法使用lambda方法引用方式, 因为此时会错误将其父类名称解析为表名,造成错误

java
public class BaseEntity{
    private Long id;
    private Long createTime;
    private Long updateTime;
}
public class Artist extends BaseEntity{
    private String name;
    private Sex sex;
}
public void helloworld(){
    Sql.select(Artist::getName);// 正确,转换为artist.name
    Sql.select(Artist::getSex);// 正确,转换为artist.sex
    Sql.select(BaseEntity::getId);// 错误,转换为base_entity.id
}

受java语法限制, 使用lambda方法引用时, 在一次调用select(selection...)中 只能传递lambda方法引用,且只能是同一实体类下的属性,但是可以多次调用select(),最终SQL是累加到selections的

字符串

当作列来解析, 默认会进行命名转换, 如传入artistName,实际SQL为artist_name 当然 如果修改了命名策略,会按实际策略转换

说明

  • 此处当作字符串解析,其实是为了兼容lombok,使用@FieldNameConstants 可以为实体类创建所有属性的常量,这样直接调用即可在很多地方当作列来使用,很方便,也无需huiche-apt
  • @FieldNameConstants 虽然方便, 但是也存在限制, 因为他本质上是一个字符串传递, 只能获取到列, 无法获取类名, 所以转换的列信息只有列名,而没有表名,
  • lombok目前不会生成父类的属性,如果需要使用父类下的属性(列),需要将父类也标注@FieldNameConstants,
  • 因为没有表名,所以不适用于多表关联场景且存在同名字段的情况,因为无法区分是哪个表的字段
java
@FieldNameConstants
public class BaseEntity{
    private Long id;
    private Long createTime;
    private Long updateTime;
}
@FieldNameConstants
public class Artist extends BaseEntity{
    private String name;
    private Sex sex;
}
public void helloworld(){
    Sql.select(Artist.Fields.name);// 转换为name
    Sql.select(Artist.Fields.sex);// 转换为sex
    Sql.select(BaseEntity.Fields.id);// 转换为id
}

因为字符串默认当作列解析, 如果确实想select字符串当作字面量,请使用Literal.of()包裹

枚举条目

枚举这里为了易用性, 默认是当作lombok@FieldNameConstants(asEnum=true)的枚举来处理的,且这是huiche最推荐使用的方式,这种方式可以获取列名,也可以获取表名

说明

  • 虽然我们推荐这种方式, 但是他也有局限性,当前lombok不会为父类属性生成枚举常量,因此无法使用这种方式传递父类属性,
  • 跟lambda方法引用相同,无法给父类标注@FieldNameConstants(asEnum=true),因为获取的表名会是错误的
  • 相比lambda方法引用, lombok字符枚举方式,在select()时没有任何语法限制,可以跟表达式等一同传入
  • 为了避免与普通枚举冲突,我们校验了枚举类的名称必须是lombok默认生成的Fields,不要使用@FieldNameConstants(asEnum=true,innerTypeName="xxx") 来改变枚举类的名称,否则会当作普通枚举
java
@FieldNameConstants(asEnum=true)
public class BaseEntity{
    private Long id;
    private Long createTime;
    private Long updateTime;
}
@FieldNameConstants(asEnum=true)
public class Artist extends BaseEntity{
    private String name;
    private Sex sex;
}
public void helloworld(){
    Sql.select(Artist.Fields.name);// 正确,转换为artist.name
    Sql.select(Artist.Fields.sex);// 正确,转换为artist.sex
    Sql.select(BaseEntity.Fields.id);// 错误,转换为base_entity.id
}

因为枚举默认当作列解析, 如果解析失败会当作普通枚举类处理,但是存在极低的风险,极端情况下,可能会将普通枚举当作lombok枚举使用,当然,也仅在传入select()方法时才有此限制

  • 枚举名称必须为Fields,否则不会当作列解析,即不能通过@FieldNameConstants(innerTypeName="xxx")来改变名称,这是为了避免冲突
  • 枚举必须是内部类,通过getEnclosingClass()获取实体类, 如果枚举类非内部类,则不会当作列解析
  • lombok生成的枚举条目是小写的,跟实体类属性对应,枚举条目的name()可以匹配到实体类的属性,一般正常编写的普通枚举是全大写的

同时满足以上3点, 才会当作lombok解析, 普通枚举只要能够避开任意一个,就不会冲突