Skip to main content

数据库-Auto注解

AUTO注解#

服务类如果继承自框架的ModeService类,在方法上加上@Auto注解,确定好方法的返回数据类型,无需编写代码,方法体代码将在运行时自动完成编码。 返回值的设计以最大努力达成目标的理念来返回结果,只要返回值的类型符合约定情形,系统都将按要求返回结果。

  • 返回类型

返回值的设计以最大努力达成目标的理念来返回结果,支持数据集向下拆解兼容,例如返回结果集是多行记录,如果方法返回值设定为List<Map<String, Object>>或List<Record>等多行数据容器,那么就会返回全部数据,如果方法返回值设定为Map<String, Object>或者Record等单行数据容器就会返回记录集的第一行,如果方法返回值设定为List<String>、String[],那么就会返回记录集的第一列,如果方法返回值设定为简单类型如String,那么就会返回记录集的第一行的第一列。

  1. 取普通单一返回值时,可以是和字段匹配的简单类型或者可转化的简单类型,例如返回int的结果,可以定义方法返回int、long或String,因为int可以转化为long和String。
  2. 取单条记录可选返回类型有Record、Map<String, Object>和表对应的模型类型。
  3. 取多行记录可选返回类型有List<Record>、List<Map<String, Object>>、List<模型>、Record[]、模型[]。
  4. 取多行记录的第一列集合时返回类型可以是List<字段类型匹配的或可转化的简单类型>、Set<字段类型匹配的或可转化的简单类型>去重、数组,例如List<String>可以兼容各种列集合,因为其他类型都可以转换为String。
  5. 多行记录二次分组,对已经从数据库查询出的结果集合进行分组,通过@Auto注解的groupColumn参数设置分组字段,支持返回类型有Map<String, List<Record>>、Map<String, List<Map<String, Object>>>、Map<String, List<模型>>。
  6. 多行记录二次排序,对已经从数据库查询出的结果集合进行排序,通过@Auto注解的sortColumn参数设置二次排序字段,sortBy参数设置二次排序顺序。
  7. 多行记录二次去重,对已经从数据库查询出的结果集合进行去重,通过@Auto注解的groupColumn参数设置去重字段,支持返回类型有Set<Record>、Set<Map<String, Object>>、Set<模型>、

魔术方法#

方法名以get/update/delete开头,且符合以下特征时,AUTO注解自动完成业务代码。需要注意的是,代码针对的是当前ModeService实例对应的模型和表。

  • get系列方法,数量不定,按需取用

get系列方法覆盖了各类常用的单表操作模型,使用规则如下:

  1. 当方法名get后面跟随的是All/First/TopN(N为数字)/Count/Max/Sum/Min/Avg等关键字时,将自动完成字面意义的代码。 All表示全部记录,First表示第一条记录,TopN表示最前面的N条记录,Count表示记录集数量统计,Max表示求记录集中的最大值,Sum表示求记录集的累加和,Min表示求记录集中的最小值,Avg表示求记录集合的平均值。
  2. 如果需要增加进一步约束条件,支持在方法名后继续通过ByX(X为表字段名)关键字增加一个条件,但仅限一个条件。另外还可以进一步通过OrderByX(X为表字段名)关键字对结果集合进行排序。注意关键字和字段名首字母需要大写,其他字母小写,如果表字段名是包含有下划线的命名方式,直接转驼峰名即可,即下划线去掉,下划线后的单词首字母大写。且表字段名不能包含有By/OrderBy/GroupBy等会产生冲突的关键字。约束条件目前只支持默认的等于条件,后续将支持更多其他类型的条件。
  3. 针对Count/Max/Sum/Min/Avg还进一步支持在方法名后继续通过GroupByX(X为表字段名)分组统计。常规查询的GroupBy有另外的实现方式(二次分组,或者后面通过自定义SQL实现),所以GroupByX(X为表字段名)加在常规查询方法后面会被忽略。
  4. 如果get(All/First/TopN)后面跟有ByX(X为表字段名),那么需要方法提供一个入参,对应该字段,作为查询条件入参。入参必须是和字段匹配的简单类型,也支持用数组和List包装多个入参。
  5. 结果集排序支持2种方式,方法名中的OrderBy为数据库SQL层排序,即全表排序,注解中指定sortColumn,sortBy是对结果集合的二次排序,属于局部数据排序。
  • update系列方法

update目前只支持一种形式:updateByField(value, fieldMap),Field为更新条件字段名;value对应Field字段,为条件的值;fieldMap是更新内容,为Map<String, Object>类型,对应于Map<字段名, 值>;返回值为int;

  • delete系列方法

delete目前只支持一种形式:deleteByField(value),Field为删除条件字段名;value对应Field字段,为条件的值;返回值为int;

public class AutoDemoServiceImpl extends ModelService<Demo> implements AutoDemoService {
// 获取第一条数据,这里获取demo表的第一条记录
@Override
@Auto
public Record getFirst() { // 这里也可以返回Map<String, Object>和Demo类型,即用模型封装
return IfFailure.get(null);
}
// 获取前3条数据,这里获取demo表的前3条记录
@Override
@Auto
public List<Record> getTop3() {
return IfFailure.get(null);
}
// 从指定的datasource获取前4条数据,这里切换datasource到test并获取demo表的前4条记录,返回用模型封装的列表
@Override
@Auto(db = "test")
public List<Demo> getTop4() {
return IfFailure.get(null);
}
// 统计全部数据行数
@Override
@Auto
public int getCount() {
return IfFailure.get(0);
}
// 按列统计数据行数,由于hobbies列在demo表中只有5行有数据,其他行都是null,所以返回5。
@Override
@Auto
public int getCountByHobbies() {
return IfFailure.get(0);
}
// 先按列分组,再对每分组按列统计数据行数,这里先按age分组,即age相同的为一组,再分别按height统计每组行数,表里面height都有值所以统计结果就是分组后各组数量。
@Override
@Auto
public List<Map<String, Object>> getCountByHeightGroupByAge() { // 返回可以用List<Map<String, Object>>也可以用List<Record>容器
return IfFailure.get(null);
}
// 获取列的最大值,这里获取demo表中age列的最大值。
@Override
@Auto
public int getMaxByAge() {
return IfFailure.get(0);
}
// 先按列分组,再对每分组获取列最大值,这里先按age分组,即age相同的为一组,再分别获取每组的最大height。
@Override
@Auto
public List<Map<String, Object>> getMaxByHeightGroupByAge() { // 返回可以用List<Map<String, Object>>也可以用List<Record>容器
return IfFailure.get(null);
}
// 获取列的最小值,这里获取demo表中age列的最小值。
@Override
@Auto
public int getMinByAge() {
return IfFailure.get(0);
}
// 先按列分组,再对每分组获取列最小值,这里先按age分组,即age相同的为一组,再分别获取每组的最小height。
@Override
@Auto
public List<Map<String, Object>> getMinByHeightGroupByAge() { // 返回可以用List<Map<String, Object>>也可以用List<Record>容器
return IfFailure.get(null);
}
// 按列求和,这里对demo表中weight列求和。
@Override
@Auto
public int getSumByWeight() {
return IfFailure.get(0);
}
// 先按列分组,再对每分组按列求和,这里先按age分组,即age相同的为一组,再分别按weight列求和。
@Override
@Auto
public List<Map<String, Object>> getSumByWeightGroupByAge() {
return IfFailure.get(null);
}
// 按列求平均值,这里对demo表中height列求平均值,即总的平均身高。
@Override
@Auto
public float getAvgByHeight() { // 注意返回值为浮点型
return IfFailure.get(0);
}
// 先按列分组,再对每分组按列求平均值,这里先按age分组,即age相同的为一组,再分别按height列求平均值,即每组的平均身高。
@Override
@Auto
public List<Map<String, Object>> getAvgByHeightGroupByAge() {
return IfFailure.get(null);
}
// 获取全部数据,这里获取demo表的全部记录
@Override
@Auto
public List<Record> getAll() { // 这里也可以返回List<Map<String, Object>>和List<Demo>类型,即用模型封装
return IfFailure.get(null);
}
// 根据gender字段获取全部数据,这里获取demo表中,gender=入参的全部记录
@Override
@Auto
public List<Demo> getAllByGender(int gender) {
return IfFailure.get(null);
}
// 根据gender字段获取全部数据并按age升序排序,这里获取demo表中,gender=入参的全部记录并按age升序排序
@Override
@Auto()
public List<Demo> getAllByGenderOrderByAge(int gender) {
return IfFailure.get(null);
}
// 获取全部数据按gender分组,各组再按age降序排序,这里获取demo表中全部数据,按gender分为两组(0=男,1=女),每组再按age降序排序,注意返回值类型为Map<String, List<Demo>>
@Override
@Auto(groupColumn = "gender", sortColumn = "age", sortBy = Auto.Order.DESC)
public Map<String, List<Demo>> getAllGroupByGender() { // 非统计查询的GroupBy目前会被忽略,由上面的注解参数groupColumn=gender完成GroupBy
return IfFailure.get(null);
}
// 根据gender获取全部name列的数据,这里获取demo表中,gender=入参的全部记录的name列
@Override
@Auto
public List<String> getNameByGender(int gender) { //如果返回类型改为Set<String>,将进行去重操作
return IfFailure.get(null);
}
// 获取全部数据,先按age降序查询出结果,再对结果集进行二次排序,按gender升序排序。并以List<Map<String, Object>>返回,Map也可以换为Record或Demo模型。
@Override
@Auto(sortColumn = "gender", sortBy = Auto.Order.ASC, orderBy = Auto.Order.DESC)
public List<Map<String, Object>> getAllOrderByAge() {
return IfFailure.get(null);
}
// 去重数据,先查询出全部结果,再对结果集进行二次排序,按age降序排序,按name去重后以Set<Demo>返回,Demo模型也可以换为Record或Map<String, Object>容器。
@Override
@Auto(groupColumn = "name", sortColumn = "age", sortBy = Auto.Order.DESC)
public Set<Demo> getAllGroupById() { // 非统计查询的GroupBy目前会被忽略,由上面的注解参数groupColumn=name完成GroupBy
return IfFailure.get(null);
}
// 去重数据,先按status查询出全部结果,再对结果集进行二次排序,按height降序排序,按name去重后以Set<Map<String, Object>>返回,Map也可以换为Record或Demo模型。
@Override
@Auto(groupColumn = "name", sortColumn = "height", sortBy = Auto.Order.DESC)
public Set<Map<String, Object>> getAllByStatus(int status) {
return IfFailure.get(null);
}
// 根据单个条件更新记录,这里根据name字段等于'入参值'为条件,更新fields中的字段
@Override
@Auto
public int updateByName(String name, Map<String, Object> fields){
return IfFailure.get(0);
}
// 根据单个条件更新记录,这里根据name字段符合'入参数组'中的值为条件,更新fields中的字段
@Override
@Auto
public int updateByName(String[] name, Map<String, Object> fields){
return IfFailure.get(0);
}
// 根据单个条件更新记录,这里根据name字段符合'入参列表'中的值为条件,更新fields中的字段
@Override
@Auto
public int updateByName(List<String> name, Map<String, Object> fields){
return IfFailure.get(0);
}
// 根据单个条件删除记录,这里根据name字段等于'入参值'为条件,删除符合的记录
@Override
@Auto
public int deleteByName(String name){
return IfFailure.get(0);
}
// 根据单个条件删除记录,这里根据name字段符合'入参数组'中的值为条件,删除符合的记录
@Override
@Auto
public int deleteByName(String[] name){
return IfFailure.get(0);
}
// 根据单个条件删除记录,这里根据name字段符合'入参列表'中的值为条件,删除符合的记录
@Override
@Auto
public int deleteByName(List<String> name){
return IfFailure.get(0);
}
}

SQL/SQL模版#

@Auto注解在没有设置value时,按魔术方法规则对方法名进行判断,一旦设置了value就会失去魔术方法名规则。value可以是对应于SQL模版中的sqlId,也可以是直接的SQL语句,区别仅仅是Sql的存放位置。当用@Auto注解时,SQL语句中的表名可以用_TABLE_代替当前表名,这样无须担心将来修改表名后需要大量修改SQL的问题。 @Auto注解对SQL语句中的参数占位符 "?" 进行了增强,支持 IN ( ? )、Where ? 、Order By ? 、Group By ? 等工作模式。需要注意的是,通过@Auto注解读取SQL模版文件中的SQL时,不支持#param(0)、#param("name")方式的参数,只支持 "?" 占位符。

@Service
public class AutoAdvancedServiceImpl extends ModelService<Demo> implements AutoAdvancedService {
// 对当前表的age列求和
@Override
@Auto("select sum(age) from _TABLE_")
public Integer getSumByAge() {
return IfFailure.get(null);
}
// 按性别统计当前表的记录行数,支持不定参数形式入参
@Override
@Auto("select count(*) from _TABLE_ where gender in (?) ")
public int getCountByGender(int... gender){
return IfFailure.get(0);
}
// 按性别统计当前表的记录行数,支持List入参
@Override
@Auto("select count(*) from _TABLE_ where gender in (?) ")
public int getCountByGender(List<Integer> gender){
return IfFailure.get(0);
}
// 获取当前表的age列,用数组返回
@Override
@Auto("select age from _TABLE_")
public Integer[] getAllAge(){
return IfFailure.get(null);
}
// 获取当前表的name和age列,用Record[]数组返回
@Override
@Auto("select name,age from _TABLE_")
public Record[] getRecordArray() { return IfFailure.get(null); }
// 获取当前表的name和gender列,用Demo[]模型数组返回
@Override
@Auto("select name,gender from _TABLE_")
public Demo[] getModelArray() { return IfFailure.get(null); }
// 根据age条件获取全部数据并按指定要求排序,返回List<Demo>模型列表
@Override
@Auto("select * from _TABLE_ where age > ? order by ?")
public List<Demo> getListByAgeWithOrderBy(int age, String orderBy) { return IfFailure.get(null); }
// 根据age条件获取全部数据并按指定要求进行分组统计,返回List<Map<String, Object>>
@Override
@Auto("select gender, GROUP_CONCAT(name) from _TABLE_ where age > ? group by ?")
public List<Map<String, Object>> getListByAgeWithGroupBy(int age, String groupBy) { return IfFailure.get(null); }
// 根据gender条件获取name,gender,age列,并按created字段降序排序,然后再对返回的数据集合按gender进行二次分组,再对每组按age进行二次排序
@Override
@Auto(value = "SELECT name,gender,age FROM _TABLE_ where gender in (?) ORDER BY created DESC", groupColumn = "gender", sortColumn = "age")
public Map<String, List<Map<String, Object>>> getAllWithOrderByAndGroupBy(List<Integer> gender) { return IfFailure.get(null); }
// 按多种条件带入方式获取数据并按指定方式排序,最后分页返回Page<Demo>
@Override
@Auto("select * from _TABLE_ where ? and (height in (?) or weight in (?)) order by ?")
public Page<Demo> getAllByPage(int page, int size, String where, List<Integer> height, List<Integer> weight, String orderBy){
return IfFailure.get(null);
}
// 子查询和关联查询,默认是支持各种子查询和多表关联查询的,但不建议这样操作
@Override
@Auto("select (select count(*) from _TABLE_) count,a.* from (select * from _TABLE_) a left join (select * from _TABLE_) b on a.id = b.id order by created desc")
public Page<Record> getDataWithComplexQuery(int page, int size){ return IfFailure.get(null); }
// 根据入参插入一条记录到当前表
@Override
@Auto("insert into _TABLE_(name, gender, age) value(?,?,?)")
public Boolean insertRecord(String name, int gender, int age){ return IfFailure.get(null); }
// 根据name条件,按入参更新匹配的记录
@Override
@Auto("update _TABLE_ set age = ?, height = ?, weight = ? where name in ( ? )")
public Boolean updateRecord(int age, int height, int weight, List<String> name){ return IfFailure.get(null); }
// 根据name条件删除匹配的记录
@Override
@Auto("delete from _TABLE_ where name in ( ? )")
public Boolean deleteRecord(String... name){ return IfFailure.get(null); }
// 从当前数据源的SQL模版获取forAuto.getByName的SQL,带入name参数查询一条记录
@Override
@Auto("forAuto.getByName")
public Demo getByName(String name) { return IfFailure.get(null); }
// 执行指定数据源的指定SQL获取数据,如果不指定默认主数据源main,每个数据源可以加载自己的SQL模版
// 这里从test数据源的模版(即test.sql)获取forAuto.insertRecord的SQL,带入name、gender、age参数插入一条记录到当前表
@Override
@Auto(db = "test", value = "forAuto.insertRecord")
public Boolean insertRecord1(String name, int gender, int age){ return IfFailure.get(null); }
// 从当前数据源的SQL模版获取forAuto.updateRecord的SQL,根据name条件,带入age、height、weight参数更新匹配的记录
@Override
@Auto("forAuto.updateRecord")
public Boolean updateRecord1(int age, int height, int weight, List<String> name){ return IfFailure.get(null); }
// 从当前数据源的SQL模版获取forAuto.deleteRecord的SQL,根据name条件删除匹配的记录
@Override
@Auto("forAuto.deleteRecord")
public Boolean deleteRecord1(String... name){ return IfFailure.get(null); }
// 执行指定数据源的指定SQL获取数据,如果不指定默认主数据源main,每个数据源可以加载自己的SQL模版
// 这里从test数据源的模版(即test.sql)获取forAuto.getByAgeAndNameLike的sql,带入age和name入参并执行,返回结果集合。
@Override
@Auto(db = "test", value = "forAuto.getByAgeAndNameLike")
public List<Demo> getByAgeAndNameLike(int age, String name) { return IfFailure.get(null); }
}

@Auto注解的SQL模版方式和上面的非模版方式差别较小,仅仅只是SQL的获取方式不同,其他都一样,这里不再进行更多演示。以上只列举部分重点内容,后续逐步完善更多具体的文档,并对重点内容进行专题描述。