查询字段(selection)
SELECT 语句的 select子句 支持传入多种类型当作 selection
表达式/函数/列/表等
支持org.huiche.ast.expression.Expression 及其所有子类,Expression带有多种内置支持的SQL语法支持,可以进行简单的SQL运算或操作
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后直接调用
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的子类
标量子查询
包括org.huiche.ast.statement.Select Select语句 和 org.huiche.ast.expression.SubQueryExpression 子查询表达式
需要自行确保其为标量子查询(一行一列)
说明
Select语句执行.asSubQuery()即转换为SubQueryExpression对象,无别名子查询Select语句执行.as(alias)会转换为DerivedTableRef对象,是带有别名的子查询(派生表),无法当作selection
实体类Class
当作表,转换为table.*
排除实体类某些字段
Sql.execludeColumn 可能会有所帮助,建议如果实体类属性比较多,需要排除其中几个属性时使用
record User(Long id,String name,String password){}
Sql.select(Sql.execludeColumn(User.class,User::password));
// 实际会select User::id,User::name,而忽略User::passwordlambda方法引用
当作列,Entity::field转换为table.column
注意, 如果实体类存在父级对象,即某些属性定义在父类中, 那父类属性无法使用lambda方法引用方式, 因为此时会错误将其父类名称解析为表名,造成错误
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, - 因为没有表名,所以不适用于多表关联场景且存在同名字段的情况,因为无法区分是哪个表的字段
@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")来改变枚举类的名称,否则会当作普通枚举
@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解析, 普通枚举只要能够避开任意一个,就不会冲突