Skip to content

x-ream/sqli

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

Files

Permalink
Failed to load latest commit information.

sqli 简单的SQL拼写

http://sqli.xream.io

license maven Codacy Badge Gitter

WIKI

sqli/sqli-builder
sqli/sqli-core
sqli/sqli-dialect
sqli/sqli-repo

sqli-repo

使用方法

sqli仅仅是SQL的编程接口,需要整合到已有的框架或项目中,
在io.xream.x7项目里实现了和Spring-Boot/Spring-JdbcTemplate的整合

@EnableX7Repostory  // code at x7/x7-spring-boot-starter
public class App{
    main() 
    ....
}
    <dependency>
         <groupId>io.xream.x7</groupId>
         <artifactId>x7-spring-boot-starter</artifactId>
         ....
    </dependency>
更多代码片段:

@Repository
public interface FooRepository extends BaseRepository<Foo> {}

@Repository
public interface FooFindRepository extends ResultMapRepository {}

@X.Mapping("t_foo")//默认是foo
public class Foo {
    @X.Key
    private Long id;
    @X.Mapping("full_name") //默认是fullName
    private String fullName;
}

@Service
public class FooServiceImpl implements FooService {

    @Autowired
    private FooRepository fooRepository;
    @Autowired
    private FooFindRepository fooFindRepository;
    
    // 临时表, 原生SQL, 则直接注入, 不支持代理
    @Autowired
    private TemporaryRepository temporaryRepository;
    @Autowired
    private NativeRepository nativeRepository;

BaseRepository API

        1. in(property, inList) //in查询, 例如: 页面上需要的主表ID或记录已经查出后,补充查询其他表的文本说明数据时使用
        2. list(Object) //对象查列表
        3. find(Criteria) //标准拼接查询,返回对象形式记录,返回分页对象
        4. list(Criteria) //标准拼接查询,返回对象形式记录,不返回分页对象
        5. get(Id) //根据主键查询记录
        6. getOne(Object) //数据库只有一条记录时,就返回那条记录
        7. list() //无条件查全表, 几乎没使用场景
        8. creaet(Object) //插入一条, 不支持返回自增键, 建议用外部机制生成ID
        9. createBatch(List<Object>) //批量插入
        10. refresh(RefreshCondition) //根据主键更新
        11. refreshUnSafe(RefreshCondition)//不根据主键更新
        12. remove(Id)//根据主键删除
        13. removeRefreshCreate(RemoveRefreshCreate<T>) //编辑页面列表时写数据库

ResultMapRepository API

        14. find(ResultMapCriteria) //标准拼接查询,返回Map形式记录,返回分页对象
        15. list(ResultMapCriteria) //标准拼接查询,返回Map形式记录,不返回分页对象
        16. listPlainValue(Class<K>, ResultMapCriteria)//返回没有key的单列数据列表 (结果优化1)
        17. findToHandle(ResultMapCriteria, RowHandler<Map<String,Object>>) //流处理API

TemporaryRepository API

        1. creaet(Object) //插入一条
        2. createBatch(List<Object>) //批量插入, 适用于数据导入场景     
        3. findToCreate(Class, Criteria.ResultMapCriteria) //builder.resultKey("foo.fooName","foo_name"), 
                //仅在临时表需要设置foo_name, 如果不设置, sqli默认按顺序设置c0,c1...., 无法和临时表匹配
        4. createRepository(Class)
        5. dropRepository(Class) //在最后调用此API, 其他框架不会关闭连接而删除临时表
        
        提醒: 不建议基于临时表调用refresh(RefreshCondition), 建议尝试调用findToHandle(....)流处理API,
              异步更新, 用fallback替代事务

标准拼接API

    CriteriaBuilder // 返回Criteria, 查出对象形式记录
    CriteriaBuilder.ResultMapBuilder //返回ResultMapCriteria, 查出Map形式记录,支持连表查询
    RefreshBuilder //构建要更新的字段和条件
    
    没有copy构建要素, 即构建一次, 只可运行一次
    
    代码片段:
        {
            CriteriaBuilder builder = CriteriaBuilder.builder(Order.class); 
            builder.eq("userId",obj.getUserId()).eq("status","PAID");
            Criteria criteria = builer.build();
            orderRepository.find(criteria);
        }
    
        {
            CriteriaBuilder.ResultMapBuilder builder = CriteriaBuilder.resultMapBuilder();
            builder.resultKey("o.id");
            builder.eq("o.status","PAID");
            builder.beginSub().gt("o.createAt",obj.getStartTime()).lt("o.createAt",obj.getEndTime()).endSub();
            builder.beginSub().eq("o.test",obj.getTest()).or().eq("i.test",obj.getTest()).endSub();
            builder.sourceScript("FROM order o INNER JOIN orderItem i ON i.orderId = o.id");
            builder.paged(obj);
            Criteria.ResultMapCriteria resultMapCriteria = builder.build();
            omsRepository.find(resultMapCriteria);
        }
        
        {
            orderRepository.refresh(
                RefreshBuilder.builder().refresh("status","PAYING").eq("id",1).eq("status","UN_PAID").build()
            );
        }
    
    条件构建API  (CriteriaBuilder | ResultMapBuilder)
        1. and // AND 默认, 可省略,也可不省略
        2. or // OR
        3. eq // = (eq, 以及其它的API, 值为null,不会被拼接到SQL)
        4. ne // !=
        5. gt // >
        6. gte // >=
        7. lt // <
        8. lte // <=
        9. like //like %xxx%
        10. likeRight // like xxx%
        11. notLike // not like %xxx%
        12. in // in
        13. nin // not in
        14. isNull // is null
        15. nonNull // is not null
        16. x // 简单的手写sql片段, 例如 x("foo.amount = bar.price * bar.qty") , x("item.quantity = 0")
        17. beginSub // 左括号
        18. endSub // 右括号

    MAP查询结果构建API  (ResultMapBuilder)
        19. distinct //去重
        20. reduce //归并计算
                // .reduce(ReduceType.SUM, "dogTest.petId") 
                // .reduce(ReduceType.SUM, "dogTest.petId", Having.of(Op.GT, 1))
                //含Having接口 (仅仅在reduc查询后,有限支持Having)
        21. groupBy //分组
        22. resultKey //指定返回列
        23. resultKeyFunction //返回列函数支持
                // .resultKeyFunction(ResultKeyAlia.of("o","at"),"FFF(o.createAt, ?)", 100000) 
        24. resultWithDottedKey //连表查询返回非JSON格式数据,map的key包含"."  (结果优化2)
       
    连表构建API  (ResultMapBuilder)
        25. sourceScript(joinSql) //简单的连表SQL,不支持LEFT JOIN  ON 多条件; 多条件,请用API[28]
        26. sourceBuilder().source("order").alia("o") //连表里的主表
        27. sourceBuilder().source("orderItem").alia("i").join(JoinType.LEFT_JOIN)
                                          .on("orderId", JoinFrom.of("o","id")) //fluent构建连表sql
        28.               .more().[1~18] // LEFT JOIN等, 更多条件
        29. sourceBuilder().sub(....) // INNER JOIN (....) i  有限支持clickhouse等数据库
                        .alia("i").join("ANY INNER JOIN").on("orderId", JoinFrom.of("o","id")) //fluent构建连表sql
    
    分页及排序API  (CriteriaBuilder | ResultMapBuilder)
        30. sort("o.id", Direction.DESC)
        31. paged().ignoreTotalRows().page(1).rows(10).last(10000) //设置last(long),会忽略page(int); 
                                       
    更新构建API  (RefreshCondition)
        32. refresh
        
    框架优化
        sourceScript/sourceBuilder
            如果条件和返回都不包括sourceScript里的连表,框架会优化移除连接(但目标连接表有用时,中间表不会
            被移除)。
            关闭优化: builder.withoutOptimization()
        in
            每500个条件会切割出一次in查询
        
    不支持项
        in(sql) // 和连表查询及二级缓存的设计有一定的冲突
        union // 过于复杂
        
    特别提醒
        Retry和Fallback的正确姿势: add retry at controller, or remote client
        不能在代码的service和repository层加retry, 和fallback
        在不适合或没必要用分布式框架(seata, x-ream/reliable)的情况下, 做fallback的时候, 需要小心

二级缓存

在x7项目里实现了springBoot的Enable
    
@EnableX7L2Caching
public class App{
    main()

二级缓存是基于redis.multiGet的高速缓存实现。

二级缓存建议返回记录条数不超过20条。调用带二级缓存的API,返回记录条数超过了
20条,请关闭二级缓存。
如果需要开启二级缓存,所有对数据库的写操作项目都需要开启二级缓存。

支持二级缓存的BaseRepository的API:
        1. in(property, inList)
        2. list(Object)
        3. find(Criteria)
        4. list(Criteria)
        5. get(Id)
        6. getOne(Object)
    
不支持二级缓存的BaseRepository, ResultMapRepository的API:
        1. list()
        2. find(ResultMapCriteria)
        3. list(ResultMapCriteria)
        4. listPlainValue(ResultMapCriteria)
    
以上设计意味着,如果in和list查询返回记录条数超过20条, 二级缓存
会失去高速响应的效果,请务必关闭二级缓存. 
如果需要返回很多条记录,需要自定义返回列, 请使用:
    find(ResultMapCriteria)
    list(ResultMapCriteria)
    listPlainValue(ResultMapCriteria)
    
用户级的过滤
{
    CacheFilter.filter(userId);
    this.orderRepository.create(order); // refresh and remove
}

{
    CacheFilter.filter(userId);
    this.orderRepository.find(criteria);
}