https://github.com/qicosmos/ormpp
modern C++ ORM, C++17, support mysql, postgresql,sqlite
https://github.com/qicosmos/ormpp
Last synced: about 1 month ago
JSON representation
modern C++ ORM, C++17, support mysql, postgresql,sqlite
- Host: GitHub
- URL: https://github.com/qicosmos/ormpp
- Owner: qicosmos
- License: apache-2.0
- Created: 2017-11-04T07:57:11.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2025-04-02T03:26:18.000Z (about 1 month ago)
- Last Synced: 2025-04-05T10:04:52.140Z (about 1 month ago)
- Language: C++
- Size: 3.57 MB
- Stars: 1,361
- Watchers: 57
- Forks: 288
- Open Issues: 28
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
English | 中文# 一个很酷的Modern C++ ORM库----ormpp
iguana版本1.0.8
https://github.com/qicosmos/iguana.git[谁在用ormpp](https://github.com/qicosmos/ormpp/wiki), 也希望ormpp用户帮助编辑用户列表,也是为了让更多用户把ormpp用起来,也是对ormpp 最大的支持,用户列表的用户问题会优先处理。
## 目录
* [ormpp的目标](#ormpp的目标)
* [ormpp的特点](#ormpp的特点)
* [快速示例](#快速示例)
* [如何编译](#如何编译)
* [接口介绍](#接口介绍)
* [roadmap](#roadmap)
* [联系方式](#联系方式)## ormpp的目标
ormpp最重要的目标就是让c++中的数据库编程变得简单,为用户提供统一的接口,支持多种数据库,降低用户使用数据库的难度。## ormpp的特点
ormpp是modern c++(c++11/14/17)开发的ORM库,目前支持了三种数据库:mysql, postgresql和sqlite,ormpp主要有以下几个特点:1. header only
1. cross platform
1. unified interface
1. easy to use
1. easy to change database你通过ormpp可以很容易地实现数据库的各种操作了,大部情况下甚至都不需要写sql语句。ormpp是基于编译期反射的,会帮你实现自动化的实体映射,你再也不用写对象到数据表相互赋值的繁琐易出错的代码了,更酷的是你可以很方便地切换数据库,如果需要从mysql切换到postgresql或sqlite只需要修改一下数据库类型就可以了,无需修改其他代码。
## 自增主键
使用REGISTER_AUTO_KEY注册自增主键
```C++
struct person {
std::string name;
int age;
int id;
};
REGISTER_AUTO_KEY(person, id)
YLT_REFL(person, id, name, age)
```## 冲突主键
使用REGISTER_CONFLICT_KEY注册冲突主键来进行update,如果未注册冲突主键则会采用自增主键
```C++
struct student {
int code;
std::string name;
char sex;
int age;
double dm;
std::string classroom;
};
REGISTER_CONFLICT_KEY(student, code)
YLT_REFL(student, code, name, sex, age, dm, classroom)
```## 快速示例
这个例子展示如何使用ormpp实现数据库的增删改查之类的操作,无需写sql语句。
```C++
#include "dbng.hpp"
#include "mysql.hpp"//注意,使用什么数据库时就需要include对应的hpp文件,里面是对相关函数的反射封装
//#include "sqlite.hpp" //例如使用sqlite时,则包含sqlite.hpp
using namespace ormpp;struct person {
std::optional age; // 可以插入null值
std::string name;
int id;
static constexpr auto get_alias_field_names(alias *) {
return std::array{ylt::reflection::field_alias_t{"person_id", 0},
ylt::reflection::field_alias_t{"person_name", 1},
ylt::reflection::field_alias_t{"person_age", 2}}; // 注意: 这里需与YLT_REFL的注册顺序一致
}
static constexpr std::string_view get_alias_struct_name(student *) {
return "CUSTOM_TABLE_NAME"; // 表名默认结构体名字(person), 这里可以修改表名
}
};
REGISTER_AUTO_KEY(person, id)
REGISTER_CONFLICT_KEY(person, name)
// REGISTER_CONFLICT_KEY(person, name, age) // 如果是多个
YLT_REFL(person, id, name, age)int main() {
person p = {"test1", 2};
person p1 = {"test2", 3};
person p2 = {"test3", 4};
std::vector v{p1, p2};dbng mysql;
mysql.connect("127.0.0.1", "dbuser", "yourpwd", "testdb");
mysql.create_datatable(ormpp_auto_key{"id"});// 插入数据
mysql.insert(p);
mysql.insert(v);// 查询数据(id=1)
auto result = mysql.query_s("id=?", 1);// 获取插入后的自增id
auto id1 = mysql.get_insert_id_after_insert(p);
auto id2 = mysql.get_insert_id_after_insert(v);// 更新数据
mysql.update(p);
mysql.update(v);
mysql.update(p, "id=1");// 替换数据
mysql.replace(p);
mysql.replace(v);// 更新指定字段
// mysql.update_some<&person::name, &person::age>(p);
// mysql.update_some<&person::name, &person::age>(v);auto result = mysql.query_s();
for (auto &person : result) {
std::cout << person.id << " " << person.name << " " << person.age
<< std::endl;
}mysql.delete_records();
// transaction
mysql.begin();
for (int i = 0; i < 10; ++i) {
person s = {"tom", 19};
if (!mysql.insert(s)) {
mysql.rollback();
return -1;
}
}
mysql.commit();
return 0;
}
``````C++
enum class Color { BLUE = 10, RED = 15 };
enum Fruit { APPLE, BANANA };struct test_enum_t {
Color color;
Fruit fruit;
int id;
};
REGISTER_AUTO_KEY(test_enum_t, id)
YLT_REFL(test_enum_t, id, color, fruit)int main() {
dbng sqlite;
sqlite.connect(db);
sqlite.execute("drop table if exists test_enum_t");
sqlite.create_datatable(ormpp_auto_key{"id"});
sqlite.insert({Color::BLUE});
auto vec1 = sqlite.query();
vec1.front().color = Color::RED;
sqlite.update(vec1.front());
auto vec2 = sqlite.query();
sqlite.update({Color::BLUE, BANANA, 1}, "id=1");
auto vec3 = sqlite.query();
vec3.front().color = Color::RED;
sqlite.replace(vec3.front());
auto vec4 = sqlite.query();
sqlite.delete_records();
auto vec5 = sqlite.query();
return 0;
}
```## 如何编译
支持的选项如下:
1. ENABLE_SQLITE3
2. ENABLE_MYSQL
3. ENABLE_PGcmake -B build -DENABLE_SQLITE3=ON -DCMAKE_BUILD_TYPE=Debug
cmake --build build --config Debug## 作为第三方库引入
mysql
```cmake
set(ENABLE_MYSQL ON)
add_definitions(-DORMPP_ENABLE_MYSQL)
add_subdirectory(ormpp)
```
或者
```cmake
set(ENABLE_MYSQL ON)
add_definitions(-DORMPP_ENABLE_MYSQL)
add_library(ormpp INTERFACE)
include(ormpp/cmake/mysql.cmake)
target_link_libraries(ormpp INTERFACE ${MYSQL_LIBRARY})
target_include_directories(ormpp INTERFACE ormpp ormpp/ormpp ${MYSQL_INCLUDE_DIR})
```sqlite
```cmake
set(ENABLE_SQLITE3 ON)
add_definitions(-DORMPP_ENABLE_SQLITE3)
add_subdirectory(ormpp)
```
或者
```cmake
set(ENABLE_SQLITE3 ON)
add_definitions(-DORMPP_ENABLE_SQLITE3)
add_subdirectory(ormpp/thirdparty)
add_library(ormpp INTERFACE)
target_link_libraries(ormpp INTERFACE sqlite3)
target_include_directories(ormpp INTERFACE ormpp ormpp/ormpp ormpp/thirdparty/sqlite3)
```pg
```cmake
set(ENABLE_PG ON)
add_definitions(-DORMPP_ENABLE_PG)
add_subdirectory(ormpp)
```
或者
```cmake
set(ENABLE_PG ON)
add_definitions(-DORMPP_ENABLE_PG)
add_library(ormpp INTERFACE)
include(ormpp/cmake/pgsql.cmake)
target_link_libraries(ormpp INTERFACE ${PGSQL_LIBRARY})
target_include_directories(ormpp INTERFACE ormpp ormpp/ormpp ${PGSQL_INCLUDE_DIR})
```### 编译器支持
需要支持C++17的编译器, 要求的编译器版本:linux gcc7.2, clang4.0; windows >vs2017 update5
### 数据库的安装
因为ormpp支持mysql和postgresql,所以需要安装mysql,postgresql,postgresql官方提供的libpq,安装之后,在CMakeLists.txt配置目录和库路径(默认安装不需要)。
## 接口介绍
ormpp屏蔽了不同数据库操作接口的差异,提供了统一简单的数据库操作接口,具体提供了数据库连接、断开连接、创建数据表、插入数据、更新数据、删除数据、查询数据和事务相关的接口。### 接口概览
```c++
//连接数据库
template
bool connect(Args&&... args);//断开数据库连接
bool disconnect();//创建数据表
template
bool create_datatable(Args&&... args);//插入单条数据
template
int insert(const T& t, Args&&... args);//插入多条数据
template
int insert(const std::vector& t, Args&&... args);//替换单条数据
template
int replace(const T &t, Args &&...args);//替换多条数据
template
int replace(const std::vector &v, Args &&...args);//更新单条数据
template
int update(const T& t, Args&&... args);//更新多条数据
template
int update(const std::vector& t, Args&&... args);//更新单条数据(指定字段)
template
int update_some(const T &t, Args &&...args);//更新多条数据(指定字段)
template
int update_some(const std::vector &v, Args &&...args);//获取插入后的自增id
template
uint64_t get_insert_id_after_insert(const T &t, Args &&...args);//删除数据(带预处理)
template
int delete_records_s(const std::string &str = "", Args &&...args);//查询数据,包括单表查询和多表查询(带预处理)
template
std::vector query_s(const std::string &str = "", Args &&...args);//删除数据(不带预处理)
template
[[deprecated]] bool delete_records(Args &&...where_condition)//查询数据,包括单表查询和多表查询(不带预处理)
template
[[deprecated]] std::vector query(Args &&...args);//执行原生的sql语句
int execute(const std::string& sql);//开始事务
bool begin();//提交事务
bool commit();//回滚
bool rollback();
```### 具体的接口使用介绍
先在entity.hpp中定义业务实体(和数据库的表对应),接着定义数据库对象:```C++
#include "dbng.hpp"
//#include "mysql.hpp"...等等,别忘记了
using namespace ormpp;struct person {
int id;
std::string name;
std::optional age; // 插入null值
static constexpr std::string_view get_alias_struct_name(student *) {
return "CUSTOM_TABLE_NAME";
}
};
REGISTER_AUTO_KEY(person, id)
YLT_REFL(person, id, name, age)int main(){
dbng mysql;
dbng sqlite;
dbng postgres;
//......
}
```1. 连接数据库
```cpp
template
bool connect(Args&&... args);
```connect exmple:
```C++
// mysql.connect(host, dbuser, pwd, dbname);
mysql.connect("127.0.0.1", "root", "12345", "testdb");
// mysql.connect(host, dbuser, pwd, dbname, timeout, port);
mysql.connect("127.0.0.1", "root", "12345", "testdb", 5, 3306);
postgres.connect("127.0.0.1", "root", "12345", "testdb");sqlite.connect("127.0.0.1", "testdb");
```返回值:bool,成功返回true,失败返回false.
2. 断开数据库连接
```cpp
bool disconnect();
```disconnect exmple:
```c++
mysql.disconnect();postgres.disconnect();
sqlite.disconnect();
```注意:用户可以不用显式调用,在数据库对象析构时会自动调用disconnect接口。
返回值:bool,成功返回true,失败返回false.
3. 创建数据表
```C++
template
bool create_datatable(Args&&... args);
```create_datatable example:
```C++
//创建不含主键的表
mysql.create_datatable();postgres.create_datatable();
sqlite.create_datatable();
//创建含主键和not null属性的表
ormpp_key key1{"id"};
ormpp_not_null not_null{{"id", "age"}};person p = {1, "test1", 2};
person p1 = {2, "test2", 3};
person p2 = {3, "test3", 4};mysql.create_datatable(key1, not_null);
postgres.create_datatable(key1, not_null);
sqlite.create_datatable(key1);
```注意:目前只支持了key、unique和not null属性。
```
mysql.create_datatable(ormpp_unique{{"name"}});
当在mysql中使用由unique声明的std::string成员创建表时,
由于"BLOB/TEXT column 'NAME' used in key specification without a key length",
故在创建表时,如果是由unique声明的std::string成员对应的数据类型则为VARCHAR(512),否则则为TEXT
```返回值:bool,成功返回true,失败返回false.
4. 插入单条数据
```C++
template
int insert(const T& t, Args&&... args);
```insert example:
```C++
person p = {1, "test1", 2};
TEST_CHECK(mysql.insert(p)==1);
TEST_CHECK(postgres.insert(p)==1);
TEST_CHECK(sqlite.insert(p)==1);// age为null
person p = {1, "test1", {}};
TEST_CHECK(mysql.insert(p)==1);
TEST_CHECK(postgres.insert(p)==1);
TEST_CHECK(sqlite.insert(p)==1);
```返回值:int,成功返回插入数据的条数1,失败返回INT_MIN.
5. 插入多条数据
```C++
template
int insert(const std::vector& t, Args&&... args);
```multiple insert example:
```C++
person p = {1, "test1", 2};
person p1 = {2, "test2", 3};
person p2 = {3, "test3", 4};
std::vector v1{p, p1, p2};TEST_CHECK(mysql.insert(v1)==3);
TEST_CHECK(postgres.insert(v1)==3);
TEST_CHECK(sqlite.insert(v1)==3);
```返回值:int,成功返回插入数据的条数N,失败返回INT_MIN.
6. 更新单条数据
```C++
template
int update(const T& t, Args&&... args);
```update example:
```C++
person p = {1, "test1", 2};
TEST_CHECK(mysql.update(p)==1);
TEST_CHECK(postgres.update(p)==1);
TEST_CHECK(sqlite.update(p)==1);
```注意:更新会根据表的key字段去更新,如果表没有key字段的时候,需要指定一个更新依据字段名,比如
```C++
TEST_CHECK(mysql.update(p, "age")==1);
TEST_CHECK(postgres.update(p, "age")==1);
TEST_CHECK(sqlite.update(p, "age")==1);
```返回值:int,成功返回更新数据的条数1,失败返回INT_MIN.
7. 更新多条数据
```C++
template
int update(const std::vector& t, Args&&... args);
```multiple insert example:
```C++
person p = {1, "test1", 2};
person p1 = {2, "test2", 3};
person p2 = {3, "test3", 4};
std::vector v1{p, p1, p2};TEST_CHECK(mysql.update(v1)==3);
TEST_CHECK(postgres.update(v1)==3);
TEST_CHECK(sqlite.update(v1)==3);
```注意:更新会根据表的key字段去更新,如果表没有key字段的时候,需要指定一个更新依据字段名,用法同上。
返回值:int,成功返回更新数据的条数N,失败返回INT_MIN.
8. 删除数据
```cpp
template
int delete_records_s(const std::string &str = "", Args &&...args);
```delete_records_s example:
```C++
//删除所有数据
TEST_REQUIRE(mysql.delete_records_s());
TEST_REQUIRE(postgres.delete_records_s());
TEST_REQUIRE(sqlite.delete_records_s());//根据条件删除数据
TEST_REQUIRE(mysql.delete_records_s("id=?", 1));
TEST_REQUIRE(postgres.delete_records_s("id=$1", 1));
TEST_REQUIRE(sqlite.delete_records_s("id=?", 1));
```返回值:bool,成功返回true,失败返回false.
9. 查询数据
```C++
template
std::vector query_s(const std::string &str = "", Args &&...args);
```query_s example:
```C++
auto result = mysql.query_s();
TEST_CHECK(result.size()==3);auto result1 = postgres.query_s();
TEST_CHECK(result1.size()==3);auto result2 = sqlite.query_s();
TEST_CHECK(result2.size()==3);//可以根据条件查询
auto result3 = mysql.query_s("id=?", 1);
TEST_CHECK(result3.size()==1);auto result4 = postgres.query_s("id=$1", 2);
TEST_CHECK(result4.size()==1);auto result5 = sqlite.query_s("id=?", 3);
```返回值:std::vector,成功vector不为空,失败则为空.
10. 特定列查询
```C++
template
std::vector query_s(const std::string &str = "", Args &&...args);
```some fields query_s example:
```C++
auto result = mysql.query_s>("select code, name, dm from person");
TEST_CHECK(result.size()==3);auto result1 = postgres.query_s>("select code, name, dm from person");
TEST_CHECK(result1.size()==3);auto result2 = sqlite.query_s>("select code, name, dm from person");
TEST_CHECK(result2.size()==3);auto result3 = mysql.query_s>("select count(1) from person");
TEST_CHECK(result3.size()==1);
TEST_CHECK(std::get<0>(result3[0])==3);auto result4 = postgres.query_s>("select count(1) from person");
TEST_CHECK(result4.size()==1);
TEST_CHECK(std::get<0>(result4[0])==3);auto result5 = sqlite.query_s>("select count(1) from person");
TEST_CHECK(result5.size()==1);
TEST_CHECK(std::get<0>(result5[0])==3);
```返回值:std::vector>,成功vector不为空,失败则为空.
11. 执行原生sql语句
```C++
int execute(const std::string& sql);
```execute example:
```C++
r = mysql.execute("drop table if exists person");
TEST_REQUIRE(r);r = postgres.execute("drop table if exists person");
TEST_REQUIRE(r);r = sqlite.execute("drop table if exists person");
TEST_REQUIRE(r);
```注意:execute接口支持的原生sql语句是不带占位符的,是一条完整的sql语句。
返回值:int,成功返回更新数据的条数1,失败返回INT_MIN.
12. 事务接口
开始事务,提交事务,回滚
```C++
//transaction
mysql.begin();
for (int i = 0; i < 10; ++i) {
person s = {i, "tom", 19};
if(!mysql.insert(s)){
mysql.rollback();
return -1;
}
}
mysql.commit();
```
返回值:bool,成功返回true,失败返回false.13. 面向切面编程AOP
定义切面:
```C++
struct log{
//args...是业务逻辑函数的入参
template
bool before(Args... args){
std::cout<<"log before"<
bool after(T t, Args... args){
std::cout<<"log after"<
bool before(Args... args){
std::cout<<"validate before"<
bool after(T t, Args... args){
std::cout<<"validate after"< mysql;
auto r = mysql.warper_connect("127.0.0.1", "root", "12345", "testdb");
TEST_REQUIRE(r);
```## roadmap
1. 支持组合键。
1. 多表查询时增加一些诸如where, group, oder by, join, limit等常用的谓词,避免直接写sql语句。
2. 增加日志
3. 增加获取错误消息的接口
4. 支持更多的数据库
5. 增加数据库链接池## 联系方式
qq群: 492859173
[http://purecpp.cn/](http://purecpp.cn/ "purecpp")
[https://github.com/qicosmos/ormpp](https://github.com/qicosmos/ormpp "ormpp")