自定义数据库操作

自定义类型

QtDao中允许使用自定义类型,定义数据表时配置如下:

<?xml version="1.0" encoding="utf-8"?>
<dao prefix="ts_" db="mysql">
    <tb name="TestTb">
        <item name="qtType" type="custom" customType="QRect" note="qt default supported type for serialize">
        <item name="myStruct" type="custom" customtype="MyStruct">
        <item name="collectionType" type="custom" customtype="QHash&lt;QString, QList&lt;MyStruct&gt;&gt;">
    </tb>
</dao>

其工作原理是,使用QDataStream存储时进行序列化为QByteArray类型,读取时反序列化为定义类型,使用的数据库类型为blob/binary。因此,使用自定义类型时确保定义的类型可反序列化,通常情况下,你需要实现"QDataStream::operator<<""QDataStream::operator>>"操作符,另外还需要实现自身的"operator=="用于模型类等于操作符。如下示例:

#include <qdatastream.h>
#include <qdatetime.h>

struct MyStruct {
    QString name;
    int id;
    QDateTime dateTime;

    bool operator==(const MyStruct& other) const {
        return other.name == this->name 
            && other.id == this->id 
            && other.dateTime == this->dateTime;
    }
};

inline QDataStream& operator<<(QDataStream& out, const MyStruct& data) {
    out << data.name << data.id << data.dateTime;
    return out;
}

inline QDataStream& operator>>(QDataStream& in, MyStruct& data) {
    in >> data.name >> data.id >> data.dateTime;
    return in;
}

Qt中有默认有许多类型自身实现了QDataStream序列化操作符,参考Qt文档Serializing Qt Data Types,因此在使用时可以直接使用,无需再实现"operator<<""operator>>"操作符。

自定义条件

QtDao无法直接使用绝大多数的sql内置函数,这时使用自定义条件构造函数来解决此问题。使用_fun()函数构造自定义条件查询语句,可以用于select查询中的columnfilteron(join查询)、_having等函数。如下示例调用sqlite的random函数:

dao::_select<User>()
    .column(_fun("random() as Random"))
    .build().one();
  • 使用字段和值

QtDao中使用QString的arg函数拼接字符串,因此,在条件表达式中使用'%'占位符为字段占位。使用field()函数传入字段:

User::Field field;

dao::_select<User>()
    .column(_fun("sum(%1) + max(%2)").field(field.score, field.age))
    .build().one();

值绑定使用的是位置占位符'?',使用value()函数传入值:

User::Field field;

dao::_select<User>()
    .column(_fun("sum(%1) + ?").field(field.score).value(60))
    .build().one();
  • 嵌套子查询

使用from函数可以构造子查询条件,其中使用'%'占位符对应子查询语句:

StudentTb::Field stuField;

auto namesQuery = dao::_select<StudentTb>()
    .column(stuField.name)
    .filter(stuField.name.like("Alice%"))
    .build();

auto result = dao::_select<StudentTb>()
    .column(stuField.name, stuField.score)
    .filter(
        _fun("%1 in %2").field(stuField.name).from(namesQuery)
    )
    .build().list();
  • 读取结果

如果在字段列表column函数中构造表达式,读取结果使用对应实例的__getExtra获取,若使用as关键字,则读取时使用as重命名后的字符串读取值:

User::Field field;

auto user = dao::_select<User>()
    .column(_fun("sum(%1)").field(field.score))
    .build().one();

int sumScore = user.__getExtra("sum(score)").toInt();

auto user2 = dao::_select<User>()
    .column(_fun("sum(%1) as sumScore").field(field.score))
    .build().one();

sumScore = user.__getExtra("sumScore").toInt();

自定义结果处理

QtDao可以方便的将查询结果直接转换为实体对象列表,但对于查询结果数据量太大时,大量的时间会被花费在对象转换操作上。对于这种情况下,使用column()函数明确指定查询部分字段将大幅减少查询时间,即便如此,有时其转换效率也不尽如意。当不需要转换为对象,自主处理查询结果时,可以极大的减少查询时间。

template<typename E>
Select<E> dao::_select<E>().build().raw(std::function<void(QSqlQuery&)> callback);

使用raw()函数,不经过对象转换,直接读取结果:

User::Fields field;
dao::_select<User>()
    .filter(field.age < 18)
    .build().raw([&](QSqlQuery& query) {
        while (query.next()) {
            //...
        }
    });

自定义查询语句

除了使用raw函数手动处理查询结果的方法外,使用BaseQuery::queryPrimitive完全自定义查询:

static void BaseQuery::queryPrimitive(
    const QString& statement, 
    std::function<void(QSqlQuery& query)> callback = nullptr,
    std::function<void(QString)> failCallback = nullptr
);

static void BaseQuery::queryPrimitive(
    const QString& statement,
    const QVariantList& values,
    std::function<void(QSqlQuery& query)> callback = nullptr,
    std::function<void(QString)> failCallback = nullptr,
);

statement传入sql语句,如果sql语句中使用占位符'?',则使用values传入值列表。callback回调用于处理查询结果,failCallback用于处理失败情况,若省略则交给统一异常处理回调:

dao::BaseQuery::queryPrimitive("select 10 + 20", [&](QSqlQuery& query) {
    if (query.next()) {
        int result = query.value(0).toInt();
    }
}, [&](QString err) {
    //...
});

与其他查询一样,该查询方式中的数据库连接受连接池管理。

results matching ""

    No results matching ""