C++编程实践——odb库的应用

C++中ODB ORM框架应用解析

一、介绍和库安装

ODB是一个开源、跨平台、跨数据库的C++对象关系映射(ORM)系统。它可以将C++对象持久化到关系数据库中,而不必处理复杂的表、列或SQL的直接操作。同时,它支持使用命令或Cmake进行直接的代码生成,而无需手动编写任何映射代码。ODB支持SQLite、PostgreSQL、MySQL、Oracle和Microsoft SQL Server关系数据库。它还附带支持了Boost和Qt的可选配置文件,并支持在持久C++类中无缝使用这些库中的值类型、容器和智能指针。一些更具体的功能,请参看ODB的官网“https://codesynthesis.com/products/odb/”中的文档说明。
其安装的过程如下:

  1. 安装Sqlite3的数据库开发支持
    sudo apt-get install libsqlite3-dev #数据库
  2. 安装ODB的开发支持
    sudo apt install odb #数据库对象关系映射框架

二、应用例程

下面是一个相对简单的例程:

//CMakeLists.txt文件
cmake_minimum_required(VERSION 3.16)

project(odb-create-db LANGUAGES CXX)

set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_CXX_COMPILER "g++")
set(CMAKE_CXX_FLAGS "-std=c++11 -g")

MESSAGE( STATUS "CMAKE_GENERATOR: ${CMAKE_INCLUDE_CURRENT_DIR}")
MESSAGE( STATUS "CMAKE_CUR_SRC_DIR: ${CMAKE_CURRENT_SOURCE_DIR}")

find_package(Qt6 6.5 REQUIRED COMPONENTS Core)

ADD_CUSTOM_COMMAND (OUTPUT demo-odb.cxx demo-odb.hxx demo-odb.ixx
  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/demo.h
  COMMAND odb
  ARGS --std c++11 --database sqlite --generate-query --generate-schema ${CMAKE_CURRENT_SOURCE_DIR}/demo.h
)

set_property(SOURCE demo-odb.cxx PROPERTY SKIP_AUTOMOC ON)
set_property(SOURCE demo-odb.hxx PROPERTY SKIP_AUTOMOC ON)

add_executable(odb-create-db main.cpp
    demo.h
    ${CMAKE_CURRENT_BINARY_DIR}/demo-odb.cxx)
target_link_libraries(odb-create-db odb odb-sqlite)

include(GNUInstallDirs)
install(TARGETS odb-create-db
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
//数据库表demo的映射文件demo.h
#ifndef DEMO_H
#define DEMO_H

#include <memory>
#include <odb/core.hxx>
#include <string>

#pragma db object pointer(std::shared_ptr) session
class Demo {
public:
  explicit Demo(const std::string &name) : m_userName(name) {}

public:
  void setName(const std::string &name) { m_userName = name; }
  unsigned int userId() const { return m_userId; }
  std::string userName() const { return m_userName; }

private:
  friend class odb::access;
  Demo() {}

private:
#pragma db id auto
  unsigned int m_userId = 0;
  std::string m_userName = "";
};
#endif // DEMO_H
//主文件main.cpp

#include <iostream>
#include <memory>  
#include <odb/connection.hxx>
#include <odb/database.hxx>
#include <string>

#include <odb/connection.hxx>
#include <odb/schema-catalog.hxx>
#include <odb/sqlite/database.hxx>
#include <odb/transaction.hxx>

#include <odb/session.hxx>
#include <odb/transaction.hxx>

#include "demo-odb.hxx"
#include "demo.h"

inline std::unique_ptr<odb::database> create_database(int &argc, char *argv[]) {
  using namespace std;
  using namespace odb::core;

  unique_ptr<database> db(new odb::sqlite::database(argc, argv, false, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE));

  {
    connection_ptr c(db->connection());

    c->execute("PRAGMA foreign_keys=OFF");

    transaction t(c->begin());
    schema_catalog::create_schema(*db);
    t.commit();

     c->execute("PRAGMA foreign_keys=ON");
  }

  return db;
}

using namespace std;
using namespace odb::core;
int main(int argc, char *argv[]) {
  try {
    std::unique_ptr<odb::database> db(create_database(argc, argv));

    {
      shared_ptr<Demo> d1(new Demo("alice"));
      shared_ptr<Demo> d2(new Demo("mary"));
      transaction t(db->begin());

      db->persist(d1);
      db->persist(d2);

      t.commit();
    }
    {
      shared_ptr<Demo> d3(new Demo("dog1"));
      shared_ptr<Demo> d4(new Demo("dog2"));
      transaction t(db->begin());

      db->persist(d3);
      db->persist(d4);

      t.commit();
    }

    {
      typedef odb::query<Demo> query;
      typedef odb::result<Demo> result;

      session s;
      transaction t(db->begin());

      result r(db->query<Demo>(query::userName == "John" || query::userName == "Jane"));
      std::cout << "query result, count = " << r.empty() << std::endl;

      for (auto it(r.begin()); it != r.end(); ++it) {
        cout << it->userName() << ' ' << it->userId() << endl;
      }


      t.commit();
    }

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

  return 0;
}

这个例程提供了数据库、表的创建,以及数据的插入和对相关数据的过滤查询。代码并不复杂,大家运行一下即可明白。

三、细节问题

在实际的应用中遇到几个细节的问题,需要注意一下:

  1. 生成表的字段名和变量与属性的关系
    一般来说是变量名称不带下划线的部分或属性名称,比如m_userName,生成的字段就是useName,last_,生成的字段是last
  2. 版本的支持
    目前来看,ODB版本只支持到了C++11(或许没有使用更新的版本),在编译选项中使用C++17会报编译错误
  3. 包含生成的odb编译单元文件
    在CmakeLists.txt中的add_executable内一定要记得增加${CMAKE_CURRENT_BINARY_DIR}/xxx-odb.cxx(前面的xxx代表工程中的实际的映射类名称,后面的cxx代表是C++编译单元)。否则的话可能无法自动生成CMakeLists.txt中OUTPUT路径下的文件。
  4. 库文件和表的生成
    在实际的操作中,使用代码生成了数据库和表,可以用代码进行各种操作,但使用SQLite viewer却无法打开。但使用测试例程的生成的则没有问题。后来经过反复查找测试才发现是SQLite viewer无法自动匹配相关Sqlite数据库的原因。只要打开一个正常的Sqlite数据库,然后再打开生成的不能直接打开的数据,即可正常打开。
  5. 编译中的类型转换问题
    如果在实际应用的类(或封装数据库映射类)中未包含相关的xxx-odb.hxx则会出现类型转换的错误,特别是使用智能指针时,可能会出现,数据类型的不匹配的错误。如:
    “error: invalid initialization of reference of type ‘Users* const&’ from expression of type ‘const std::shared_ptr’”或
    “error: invalid use of incomplete type ‘struct odb::query_selector_impl<Users, odb::id_common, odb::class_other>’”

四、扩展

在上面的应用中,是一种小规模的场景下。如果需要创建的表工库比较多的情况下,官网也提供了更好的示例方法,在https://github.com/BtbN/OdbCmake/tree/master的cmake/Modules中有两个cmake模块文件即findODB.cmake和useODB.cmake。大家可能参考它们写出更复杂的Sqlite的ORM映射方法。
具体的文件内容就不再贴上来了,有兴趣可以去下载。示例工程也比较简单明了,相信可以在很短的时间内掌握相关的应用方式。

五、总结

其实用C++来开发数据库应用是一种非常不划算的工作,做为一种较低级的语言,天生对数据库的支持就不好操作。虽然有ODB这种ORM框架,但如果和其它语言相比,还是要复杂的多。但在C++开发中,有些场景下数据库的应用还是不可避免的,特别是Sqlite这种小型数据库。所以,还是要掌握一些ORM的相关框架,至少比原生访问数据库要简单很多。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值