fountain实现思想与应用第四弹——ORM

项目链接:https://gitcode.com/Cangjie-SIG/fountain

这是ORM的类图

ORM的核心是SqlExecutor,所有增删改查、填充SQL参数都是这个类的API,而且各种高阶API也是它的扩展,数据库访问的接口也通过@DAO宏扩展到SqlExecutor。

SqlArgs是SqlExecutor是SQL参数集合按照调用添加参数的函数顺序添加到SqlArgs实例,每一个参数都被包装成SqlArg实例,访问数据库时遍历SqlArgs将参数添加到Statement。

类图中的RootDAO依赖的Condition结尾的类是SQL条件函数,MeetCondition执行每一个符合的条件、ChooseCondition执行第一个符合的条件,LoopCondition遍历Iterable实例。比如说可以有这样的SQL构造代码:

let page = executor.FROM<UserPO>()
.WHERE{'''
${meet(username.size > 0, ' username like '){
'%${username}%'
}}
${meet(password.size > 0, ' and "password" like '){
'%${password}%'
}}'''
}.page<UserPO>(10, page: 1)

SqlHead依赖的Clause类是SQL API的辅助工具,可以帮助开发者构造SQL。比如可以有以下API调用:

executor.FROM<MapperClass>().page<MapperClass>(10/*每页记录数*/, page: 1/*查询的页数*/)
executor.FROM<MapperClass>().WHERE{'id ${IN(list)}'}.page<MapperClass>(10)

分页查询时按照不同数据库的方言构造相应的分页查询SQL,构造的SQL会作为计数和分页查询的SQL子查询,避免有些查询SQL包含不能跟分页子句同时出现的SQL子句。比如postgres的计数SQL不能跟排序子句同时出现。

类图下面的QueryMappers和QueryMapper及它的子类是映射类型。每个映射类的成员被包装为QueryMapper实例,而QueryMappers是QueryMapper的集合。这些类型都是ORM内部类型,开发者不需要自己实例化它们,仅仅需要使用@QueryMappersGenerator修饰映射类。

@QueryMappersGenerator[user_info]//映射的表名
public class UserPO {
    @ORMField[true 'id']//如果是主键列,使用true表示,id是列名
    private var id: Int64 = 0
    @ORMField['username']//列名
    private var username: String = ''
    @ORMField['password']
    private var password: String = ''
    @ORMField['save_time']
    private var saveTime: ?DateTime = None<DateTime>
}

下面是一个完整的ORM例子。

首先初始化ORM。

import std.env
import opengauss.driver.*
import opengauss.slog
import fountain.data.*
import fountain.data.macros.*
import fountain.log.LoggerFactory
import fountain.orm.*
//具体可以参考项目里的fdemo/boot/src/boot.cj
private let _ = {=>
    let logger = LoggerFactory.getLogger("opengauss")
    slog.setDefault(logger)//初始化一个日志实例,给opengauss驱动指定日志实例
    let driver = DriverManager.getDriver("opengauss").getOrThrow()//实例化opengauss 驱动
    let url = env.getVariable('POSTGRES') ?? ''//获得opengauss url
    ORM.register(driver, url, [])//将驱动和url注册到ORM
    //register有一个default!: Bool = true参数,表示当前驱动是不是默认驱动,
    //如果多次调用ORM.register,而且都使用default默认值,则最后一次调用的驱动是默认驱动
}()//opengauss有列索引和参数索引从0开始,跟仓颉团队沟通过,回复也是应该从0开始,
//不过文档没有明确提到,有些驱动实现从1开始。
//我已经提了ISSUE希望文档能够有明确的说明。

然后声明一个Service类的实现。

import fountain.orm.*

import user.service.UserService
import user.dao.UserDAO

@Bean
public class UserServiceImpl <: UserService & RootService {
    public func register(username: String, password: String): Int64 {
        println('username: ${username}, password: ${password}')
        executor().register(username, password)//register是UserDAO的函数,UserDAO是SqlExecutor的扩展。
        //executor()是RootService接口的函数,它有默认实现,
        //功能是使用默认驱动从fountain.orm.ORM获得fountain.orm.SqlExecutor实例,SqlExecutor内部维持着数据库连接。
        //这个实例保存在ThreadLocal,同一线程重复调用executor()返回的是同一个SqlExecutor。
        //另外还有executor(driverName),使用指定驱动名创建SqlExecutor。
    }
}

最后声明一个DAO。

import fountain.orm.*
import fountain.orm.macros.*

@DAO//这个宏会将UserDAO扩展到SqlExecutor
public interface UserDAO <: RootDAO {
    func register(username: String, password: String): Int64 {
        //executor是RootDAO的属性,SqlExecutor也实现了RootDAO,executor在SqlExecutor的实现就是返回自身。
        executor.setSql('''
            insert into user_info(
                        username,
                        password)
                 values(${arg(username)},
                        ${arg(password)})
            returning id'''
        ).insert//sqlSql也是返回SqlExecutor自身,insert返回插入的ID。
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值