bergmann 7ba4e383c6 | 5 years ago | |
---|---|---|
cmake | 5 years ago | |
include | 5 years ago | |
src | 5 years ago | |
test | 5 years ago | |
.gitignore | 6 years ago | |
.gitmodules | 6 years ago | |
CMakeLists.txt | 5 years ago | |
LICENSE.txt | 6 years ago | |
README.md | 5 years ago |
A C++17 library to serialize and deserialize C++ objects to a MYSQL/MARIADB database.
Before you can use cpphibernate you need to download and install the official mariadb C connector) and the boost/hana library. And if you want to run the tests you additionally need the google testing framework.
The cpputils libary and the cppmariadb library will be automatically downladed during the build.
In this simple example we use a single and simple object, that is stored in the database. The create, update, read and destroy methods and the corresponding SQL queries are shown in the code below.
#include <memory>
#include <iostream>
#include <exception>
#include <cpphibernate.h>
#include <cpphibernate/driver/mariadb.h>
/* define the class to hibernate */
struct test_data
{
size_t id; //!< ID of the class instance (this field is mandatory for the hibernate engine)
std::string str_data; //!< Add string data field
::cpphibernate::string<64> str64_data; //!< Add a special string data field with a mex length of 64
uint32_t u32; //!< Add a normal integer data field
std::unique_ptr<uint32_t> u32_ptr_u; //!< Add a nullable integer data field
};
/* create the database schema */
constexpr decltype(auto) test_schema = cpphibernate_make_schema(
test_schema, // this is the schema name
cpphibernate_make_table_name(
tbl_test_data, // this is the table name
test_data, // this is the referenced class
1, // a unique id for the table
cpphibernate_make_id (&test_data::id), // pointer to the ID member
cpphibernate_make_field (test_data, str_data), // define a normal member field
cpphibernate_make_field (test_data, str64_data), // [...]
cpphibernate_make_field (test_data, u32), // [...]
cpphibernate_make_field (test_data, u32_ptr_u), // [...]
)
);
int main(int argc, char** argv)
{
try
{
using namespace ::cppmariadb;
using namespace ::cpphibernate;
/* establish connection to database */
connection c = database::connect("localhost", 3306, "testuser", "password", "", client_flags::empty());
/* create a hibernation context */
auto context = make_context_ptr<mariadb_driver>(test_schema, c);
/* initialize the database schema */
context.init(); /* CREATE SCHEMA IF NOT EXISTS `test_schema` DEFAULT CHARACTER SET utf8;
USE `test_schema`;
CREATE TABLE IF NOT EXISTS `tbl_test_data`
(
`tbl_test_data_id` INT UNSIGNED NOT NULL,
`str_data` VARCHAR(100) NOT NULL,
`str64_data` VARCHAR(64) NOT NULL,
`u32` INT UNSIGNED NOT NULL,
`u32_ptr_u` INT UNSIGNED NULL DEFAULT NULL,
PRIMARY KEY ( `tbl_test_data_id` ),
UNIQUE INDEX `index_tbl_test_data_id` ( `tbl_test_data_id` ASC )
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8; */
/* create some test data */
test_data data;
data.str_data = "this is a simple string";
data.str64_data = "this is a string with max 64 characters";
data.u32 = 123;
/* create a new dataset in the database:
* the new IDs of the object are stored in the corresponding members */
context.create(data); /* INSERT INTO
`tbl_test_data`
SET
`str_data`='this is a simple string',
`str64_data`='this is a string with max 64 characters',
`u32`=123,
`u32_ptr_u`=NULL; */
/* change some data and update the database */
t1.u32_ptr_u.reset(new uint32_t(456));
context.update(data); /* UPDATE
`tbl_test_data`
SET
`str_data`='this is a simple string',
`str64_data`='this is a string with max 64 characters',
`u32`=123,
`u32_ptr_u`=456
WHERE
`tbl_test_data_id`=1; */
/* read back the created object:
* if no selector is passed here, the ID member of the object is used */
context.read(data); /* SELECT "
`tbl_test_data`.`tbl_test_data_id`),
`tbl_test_data`.`str_data`,
`tbl_test_data`.`str64_data`,
`tbl_test_data`.`u32`,
`tbl_test_data`.`u32_ptr_u`
FROM
`tbl_test_data`
WHERE
`tbl_test_data`.`tbl_test_data_id`=1; */
/* delete the object from the database */
context.destroy(data); /* DELETE
`tbl_test_data`
FROM
`tbl_test_data`
WHERE
`tbl_test_data`.`tbl_test_data_id`=1; */
return 0;
}
catch(const std::exception& ex)
{
std::cout << ex.what() << std::endl;
}
return 1
}
The more advanced example uses a more complex database schema, containing custom defined enum types, one to one and one to many correleation of different tables, a polymorphic class hierarchy with inheritance and some of the std containers like std::unique_ptr or std::vector.
#include <memory>
#include <iostream>
#include <exception>
#include <cpphibernate.h>
#include <cpphibernate/driver/mariadb.h>
#include <cpputils/misc/enum.h>
#include <cpputils/container/nullable.h>
/* define a test enum */
enum class test_enum
{
test0,
test1,
test2,
test3,
first = test0,
last = test3,
};
/* define the enum to string mapping for the test enum */
DEFINE_ENUM_TO_STRING_MAP(
test_enum,
{ test_enum::test0, "test0" },
{ test_enum::test1, "test1" },
{ test_enum::test2, "test2" },
{ test_enum::test3, "test3" }
);
DEFINE_STRING_TO_ENUM_MAP(
test_enum,
invariant_string_less,
{ "test0", test_enum::test0 },
{ "test1", test_enum::test1 },
{ "test2", test_enum::test2 },
{ "test3", test_enum::test3 }
);
/* define some normal C++ structs/classes */
struct test1
{
size_t id;
std::string str_data;
::cpphibernate::string<64> str64_data;
utl::nullable<uint32_t> u32_nullable;
std::unique_ptr<uint32_t> u32_ptr_u;
std::shared_ptr<uint32_t> u32_ptr_s;
};
struct test2
{
size_t id;
uint8_t u8_data;
int8_t i8_data;
uint16_t u16_data;
int16_t i16_data;
};
struct test3
{
size_t id;
uint32_t u32_data;
int32_t i32_data;
uint64_t u64_data;
int64_t i64_data;
};
struct base
{
::cpphibernate::uuid id;
std::string name;
virtual ~base() = default;
};
struct derived1
: public base
{
::cpphibernate::uuid derived1_id;
test1 test1_data;
test_enum enum_data;
};
struct derived2
: public base
{
::cpphibernate::uuid derived2_id;
utl::nullable<test2> test2_nullable;
std::unique_ptr<test2> test2_ptr_u;
std::shared_ptr<test2> test2_ptr_s;
};
struct derived3
: public derived2
{
::cpphibernate::uuid derived3_id;
std::list<test3> test3_list;
std::vector<test3> test3_vector;
};
/* create the database schema */
constexpr decltype(auto) test_schema = cpphibernate_make_schema(
test, // this is the schema name
cpphibernate_make_table_name(
tbl_test1, // this is the table name
test1, // this is the referenced class
1, // a unique id for the table
cpphibernate_make_id (&test1::id), // pointer to the ID member
cpphibernate_make_field (test1, str_data), // define a normal member field
cpphibernate_make_field (test1, str64_data), // [...]
cpphibernate_make_field (test1, u32_nullable), // [...]
cpphibernate_make_field (test1, u32_ptr_u), // [...]
cpphibernate_make_field (test1, u32_ptr_s) // [...]
),
cpphibernate_make_table_name(
tbl_test2,
test2,
2,
cpphibernate_make_id (&test2::id),
cpphibernate_make_field (test2, u8_data),
cpphibernate_make_field (test2, i8_data),
cpphibernate_make_field (test2, u16_data),
cpphibernate_make_field (test2, i16_data)
),
cpphibernate_make_table_name(
tbl_test3,
test3,
3,
cpphibernate_make_id (&test3::id),
cpphibernate_make_field (test3, u32_data),
cpphibernate_make_field (test3, i32_data),
cpphibernate_make_field (test3, u64_data),
cpphibernate_make_field (test3, i64_data)
),
cpphibernate_make_table_name(
tbl_base,
base,
10,
cpphibernate_make_id (&base::id),
cpphibernate_make_field (base, name)
),
cpphibernate_make_table_name(
tbl_derived1,
derived1,
11,
cpphibernate_make_id (&derived1::derived1_id),
cpphibernate_make_field (derived1, test1_data),
cpphibernate_make_field (derived1, enum_data)
),
cpphibernate_make_table_name(
tbl_derived2,
derived2,
12,
cpphibernate_make_id (&derived2::derived2_id),
cpphibernate_make_field (derived2, test2_nullable),
cpphibernate_make_field (derived2, test2_ptr_u),
cpphibernate_make_field (derived2, test2_ptr_s)
),
cpphibernate_make_table_name(
tbl_derived3,
derived3,
13,
cpphibernate_make_id (&derived3::derived3_id),
cpphibernate_make_field (derived3, test3_list),
cpphibernate_make_field (derived3, test3_vector)
)
);
int main(int argc, char** argv)
{
try
{
using namespace ::cppmariadb;
using namespace ::cpphibernate;
/* establish connection to database */
connection c = database::connect("localhost", 3306, "testuser", "password", "", client_flags::empty());
/* create a hibernation context */
auto context = make_context_ptr<mariadb_driver>(test_schema, c);
/* initialize the database schema */
context.init();
/* create some test data */
derived3 d3;
d3.name = "derived3";
d3.test3_list.emplace_back();
d3.test3_list.back().u32_data = 100;
d3.test3_list.back().i32_data = 101;
d3.test3_list.back().u64_data = 102;
d3.test3_list.back().i64_data = 103;
d3.test3_list.emplace_back();
d3.test3_list.back().u32_data = 110;
d3.test3_list.back().i32_data = 111;
d3.test3_list.back().u64_data = 112;
d3.test3_list.back().i64_data = 113;
d3.test3_vector.emplace_back();
d3.test3_vector.back().u32_data = 200;
d3.test3_vector.back().i32_data = 201;
d3.test3_vector.back().u64_data = 202;
d3.test3_vector.back().i64_data = 203;
d3.test3_vector.emplace_back();
d3.test3_vector.back().u32_data = 210;
d3.test3_vector.back().i32_data = 211;
d3.test3_vector.back().u64_data = 212;
d3.test3_vector.back().i64_data = 213;
d3.test3_vector.emplace_back();
d3.test3_vector.back().u32_data = 220;
d3.test3_vector.back().i32_data = 221;
d3.test3_vector.back().u64_data = 222;
d3.test3_vector.back().i64_data = 223;
/* create a new dataset in the database:
* the new IDs of the object are stored in the corresponding members */
context.create(d3);
/* read back the created object:
* if no selector is passed here, the ID member of the object is used */
context.read(d3);
/* if we pass a pointer to the read method, the suitable object is created automatically */
using namespace ::boost::hana::literals;
using namespace ::cpphibernate::modifier;
using base_ptr_u = std::unique_ptr<base>;
base_ptr_u b;
constexpr decltype(auto) base_key_field = test_schema.tables[3_c].fields[0_c];
context.read(b, where(equal(base_key_field, d3.id)));
auto * d3_ptr = dynamic_cast<derived3*>(b.get());
if (d3_ptr)
{
/* do something with the data */
}
/* this also works for containers of pointers */
using base_vector = std::vector<base_ptr_u>;
base_vector vec;
context.read(vec);
for (auto& ptr : vec)
{
/* do something with the data */
}
return 0;
}
catch(const std::exception& ex)
{
std::cout << ex.what() << std::endl;
}
return 1
}
This project is licensed under the MIT License - see the LICENSE.txt file for details