ODB 2.ODB mysql hello example

环境:Windows, Visutal Studio 2019 MySQL 8.0

1.ODB使用简介

ODB的使用依赖于
1.ODB编译器
2.libodb(ODB 公共运行时库)
3.libodb-<database> ODB运行时特定数据库,本例中以MySQL为例
2与3已在上节中编译完成
https://blog.youkuaiyun.com/LutionYoung/article/details/143110367?spm=1001.2014.3001.5501

这三个工具的使用方式:
ODB编译器 ----> 编译xxx.hxx ----> 产生相关文件,包括c++代码 ----> c++编译器将这些代码文件编译进去并链接2与3提到的库 ----> 生成可执行文件

ODB的一个理念就是将c++类对象持久化(Making Objects Persistent)到数据库中,即将这个对象中的成员作为字段插入到数据库中,即使应用程序退出或这个类对象析构,这个类对象的数据仍然存在(即持久化)

2.example hello 工程配置

Visual Studio2019新建工程,这里是创建了一个链接Qt库的工程(并不是是使用Qt那套.pro的工程文件,仍然是.sln,创建其他c++项目也是一样的),QtWidgetsApplication2.h和QtWidgetsApplication2.cpp没有用到,可以不管。
工程文件结构:
在这里插入图片描述
工程配置:
附加包含目录:
在这里插入图片描述
common文件下放的是odb头文件、odb-mysql头文件和.lib文件,common目录下的结构:
在这里插入图片描述附加库目录:
在这里插入图片描述
附加依赖项:
在这里插入图片描述

增加了person.hxx文件,该文件由ODB工程编译。
person.hxx文件内容(文件内容可以先跳过不看,先跑通整个编译流程):

// person.hxx
#ifndef PERSON_HXX
#define PERSON_HXX

#include <string>
#include <cstddef> // std::size_t

#include <odb/core.hxx> // (1)头文件 必须包含

#pragma db model version(1, 1) // (2)定义了当前数据库结构的版本,第一个数字1代表最低版本,第二个数字代表当前版本,用于数据库结构改变时使用

/* 该语句指定了std::string类型映射为MySQL的varchar(256)类型,默认映射为TEXT类型
*  因为MySQL5.7以后就有了严格模式,规定了TEXT BOOL等类型默认不允许(也可修改为允许)有默认值
*  这会导致升级model版本时的xxxpre.sql和xxxpost.sql无法在MySQL8.0中执行
*/
#pragma db value(std::string) type("VARCHAR(256)")

#pragma db object // (3)告诉ODB编译器这是一个持久类
class person
{
public:
    // (4)构造函数 也可以是私有的
  person (const std::string& first,
          const std::string& last,
          unsigned short age)
      : first_ (first), last_ (last), age_ (age)
  {
  }

  const std::string& first () const{return first_;}

  const std::string&last () const{return last_;}

  unsigned short
  age () const
  {
    return age_;
  }

  void
  age (unsigned short age)
  {
    age_ = age;
  }

  unsigned long id() { return id_; }

private:
  friend class odb::access; // (5)让odb::access可以自由访问该类的对象

  person () {}
  // ODB会在数据库中创建id first last age这些字段,注意在类中这些成员名是带有'_'的,即字段名+'_'为成员的名称
  // (6)告诉ODB下边的id_是持久类的标识符,即同一个持久类的不同的对象,标识符都不相同
  #pragma db id auto // auto表示id是由数据库来安排的
  unsigned long id_;

  std::string first_;
  std::string last_;
  unsigned short age_;

// #pragma db default("default")  // 设置数据库中middle字段的默认值 为了在数据库结构升级时使用
  // std::string middle_ = "11111"; // 在版本(db model version)2中 增加该成员,即在数据库中增加该字段 该行设置了该持久类构造后middle_成员的默认值 持久化后会才写到数据库中 此时需要先执行xxxpre.sql 再执行xxxpost.sql后 新的应用程序就可以正常操作修改过的数据库中的person表了

};

// ODB提出view概念 使用view进行一些更复杂的查询操作
#pragma db view object(person)
struct person_stat
{
#pragma db column("count(" + person::id_ + ")")   // 数量
    std::size_t count;
#pragma db column("min(" + person::age_ + ")")    // age的最小值
    unsigned short min_age;
#pragma db column("max(" + person::age_ + ")")    // age的最大值
    unsigned short max_age;
};

#endif // PERSON_HXX

main.cpp文件内容(文件内容可以先跳过不看,先跑通整个编译流程):

#include "QtWidgetsApplication2.h"
#include <QtWidgets/QApplication>
#include <QFile>

#include <memory>   // std::auto_ptr
#include <iostream>

// ODB公共库部分头文件
#include <odb/database.hxx>
#include <odb/transaction.hxx>
// ODB MySQL部分头文件
#include <odb/mysql/database.hxx>
// 持久化对象头文件
#include "person.hxx"
#include "person-odb.hxx"

using namespace std;
using namespace odb::core;

int main(int argc, char *argv[])
{
     插入
    typedef odb::query<person> query;
    typedef odb::result<person> result;
    try
    {
        std::string user = "root";
        std::string passwd = "root";
        std::string dbName = "database1";
        std::string host = "localhost";
        unsigned int port = 3306;
        std::string socket;
        std::string charset = "utf8";
        unsigned long client_flags = 0; // CLIENT_SSL; // 客户端使用SSL 参考MYSQL C API

        auto_ptr<database> db(new odb::mysql::database(user, passwd, dbName, host, port, socket, charset));

        unsigned long john_id, jane_id, joe_id;
        // 实例化持久化对象 并给字段赋值
        {
            person john("John", "Doe", 33);
            person jane("Jane", "Doe", 32);
            person joe("Joe", "Dirt", 30);

            // 开始事务
            transaction t(db->begin());

            t.tracer(odb::stderr_tracer); // 使用tracer可打印ODB实际的SQL语句 但是值会用?来表示

            // 让这些对象在数据库中持久化 并获取他们的id
            john_id = db->persist(john);
            jane_id = db->persist(jane);
            joe_id = db->persist(joe);

            t.commit(); // 提交事务 不提交事务则数据库会回滚
            // transaction对象t 离开作用域之前也未提交 则数据库同样会回滚
        }
    }
    catch (const odb::exception& e)
    {
        cerr << e.what() << endl;

    }

     查询
    try {
        std::string user = "root";
        std::string passwd = "root";
        std::string dbName = "database1";
        std::string host = "localhost";
        unsigned int port = 3306;
        std::string socket;
        std::string charset = "utf8";
        unsigned long client_flags = 0; // CLIENT_SSL; // 客户端使用SSL 参考MYSQL C API

        auto_ptr<database> db(new odb::mysql::database(user, passwd, dbName, host, port, socket, charset));

        person john("John", "Doe", 33);
        person jane("Jane", "Doe", 32);
        person joe("Joe", "Dirt", 30);

        transaction t(db->begin());

        t.tracer(odb::stderr_tracer);

        // 查询person表中年龄大于30的
        result r(db->query<person>(query::age > 30));
        for (result::iterator i(r.begin()); i != r.end(); ++i)
        {
            // 通过person类中的成员访问具体的结果
            cout << "Hello, " << i->first() << " (" << i->id() << ")!" << endl;
        }

        t.commit();
    }
    catch (const odb::exception& e) {
        cerr << e.what() << endl;
        return 1;
    }

     修改
    try {
        std::string user = "root";
        std::string passwd = "root";
        std::string dbName = "database1";
        std::string host = "localhost";
        unsigned int port = 3306;
        std::string socket;
        std::string charset = "utf8";
        unsigned long client_flags = 0; // CLIENT_SSL; // 客户端使用SSL 参考MYSQL C API

        auto_ptr<database> db(new odb::mysql::database(user, passwd, dbName, host, port, socket, charset));

        unsigned long john_id, jane_id, joe_id;
        {
            person john("John", "Doe", 33);
            person jane("Jane", "Doe", 32);
            person joe("Joe", "Dirt", 30);
            transaction t(db->begin());
            t.tracer(odb::stderr_tracer);
            // 让这些对象在数据库中持久化 并获取他们的id
            john_id = db->persist(john);
            jane_id = db->persist(jane);
            joe_id = db->persist(joe);
            t.commit();
        }

        // 通过id修改Joe Dirt的age
        {
            transaction t(db->begin());
            t.tracer(odb::stderr_tracer);

            // 通过持久类对象的标识从数据库中获取该对象
            auto_ptr<person> joe(db->load<person>(joe_id));
            // 修改age
            joe->age(joe->age() + 1);
            // 更新该对象
            db->update(*joe);

            t.commit();
        }

        // 通过查询后修改
        {
            transaction t(db->begin());
            t.tracer(odb::stderr_tracer);

            // 如果数据库中只有一个 则可以使用query_one来查询 从而避免了迭代器遍历
            auto_ptr<person> joe(db->query_one<person>(query::first == "Joe"
                && query::last == "Dirt"));

            if (joe.get() != 0) {
                joe->age(joe->age() + 1);
                db->update(*joe);
            }

            t.commit();
        }

    }
    catch (const odb::exception& e) {
        cerr << e.what() << endl;
        return 1;
    }

     使用view进行稍微复杂的操作
    try {
        std::string user = "root";
        std::string passwd = "root";
        std::string dbName = "database1";
        std::string host = "localhost";
        unsigned int port = 3306;
        std::string socket;
        std::string charset = "utf8";
        unsigned long client_flags = 0; // CLIENT_SSL; // 客户端使用SSL 参考MYSQL C API
        auto_ptr<database> db(new odb::mysql::database(user, passwd, dbName, host, port, socket, charset));

        transaction t(db->begin());
        t.tracer(odb::stderr_tracer);

        // 查询的结果总是只有一个元素
        // 查询出总的数量 最大age 最小age 查询的结果存储在person_stat中
        person_stat ps(db->query_value<person_stat>());
        cout << "count : " << ps.count << endl
            << "min age: " << ps.min_age << endl
            << "max age: " << ps.max_age << endl;
        t.commit();
    }
    catch (const odb::exception& e) {
        cerr << e.what() << endl;
        return 1;
    }

     删除操作
    try {
        std::string user = "root";
        std::string passwd = "root";
        std::string dbName = "database1";
        std::string host = "localhost";
        unsigned int port = 3306;
        std::string socket;
        std::string charset = "utf8";
        unsigned long client_flags = 0; // CLIENT_SSL; // 客户端使用SSL 参考MYSQL C API
        auto_ptr<database> db(new odb::mysql::database(user, passwd, dbName, host, port, socket, charset));

        // 插入John Doe
        unsigned long john_id;
        {
            person john1("John", "Doe", 33);
            person john2("John", "Doe", 33);
            transaction t(db->begin());
            t.tracer(odb::stderr_tracer);
            // 让这些对象在数据库中持久化 并获取他们的id
            john_id = db->persist(john1);
            db->persist(john2);
            t.commit();
        }

        // 删除John Doe
        // 通过id删除
        {
            transaction t(db->begin());
            t.tracer(odb::stderr_tracer);
            db->erase<person>(john_id);
            t.commit();
        }

        // 查找后删除
        {
            transaction t(db->begin());
            t.tracer(odb::stderr_tracer);

            auto_ptr<person> john(db->query_one<person>(query::first == "John" &&
                query::last == "Doe"));

            if (john.get() != 0) {
                db->erase(*john);
            }

            t.commit();
        }
    }
    catch (const odb::exception& e) {
        cerr << e.what() << endl;
        return 1;
    }


    return 0;
}

person.hxx需要设置自定义生成工具,让Visual Studio2019自动调用ODB编译:
在这里插入图片描述在这里插入图片描述
配置命令行和输出:
在这里插入图片描述
命令行:
odb.exe --std c++11 --database mysql --generate-query --generate-schema %(Identity)
其中%(Identity)代表的是person.hxx
输出:
%(Filename)-odb.hxx;%(Filename)-odb.cxx;%(Filename)-odb.ixx;%(Filename).sql;%(Outputs)
因为odb编译person.hxx后会生成如下文件(还会由其他文件):

第一次编译会在目录下生成如下文件(编译应该会失败,但是ODB编译执行成功):
在这里插入图片描述
**person.sql:**用于创建person表,表的字段与person类的成员相关,要保证应用程序在执行前这张表已经创建好,在mysql中执行该文件中的sql语句:
在这里插入图片描述

**person.xml:**用于记录表结构的版本
**person.cxx.与person.hxx:**为ODB编译器生成的c++代码,需要使用Visual Studio2019添加到项目中进行编译,此时工程为:
在这里插入图片描述
执行该应用程序前,需要将odb公共库的dll和odbmysql的dll以及mysql库的dll放到exe的同级目录下:
在这里插入图片描述
然后执行应用程序,可以查看数据库中person表里已经有数据了。

3.表的结构的升级

因为数据库的结构可能会被修改,ODB支持这样的修改,例如在上边的person.hxx中,如下代码:

#pragma db model version(1, 1) // 定义了当前数据库结构的版本 第一个数字1代表最低版本 第二个数字2代表当前版本

修改为版本2:

#pragma db model version(1, 2) // 定义了当前数据库结构的版本 第一个数字1代表最低版本 第二个数字2代表当前版本

定义了表的结构的版本,并且ODB编译器会生成person.xml来记录表结构的版本,这个文件是ODB编译器自己来维护的。
当要为表增加一个字段时,解注释如下代码:

#pragma db default("default")  // 设置数据库中middle字段的默认值 为了在数据库结构升级时使用
  std::string middle_ = "11111"; // 该行设置了该持久类构造后middle_成员的默认值 持久化后会才写到数据库中

再次编译,会发现生成了两个文件:
在这里插入图片描述
先执行pre文件里的sql语句,再执行post文件里的sql语句,然后新的应用程序就可以正常使用新版本的数据库了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值