zdbpp.h - C++ Interface for libzdb

This interface provides a C++ wrapper for libzdb, offering a convenient and type-safe way to interact with various SQL databases from C++ applications.
Features
- Thread-safe Database Connection Pool
- Connect to multiple database systems simultaneously
- Zero runtime configuration, connect using a URL scheme
- Supports MySQL, PostgreSQL, SQLite, and Oracle
- Modern C++ features (C++20 or later required)
Core Concepts
The central class in this library is ConnectionPool
, which manages database connections. All other main classes (Connection
, PreparedStatement
, and ResultSet
) are obtained through the ConnectionPool
or its derivatives.
ConnectionPool and URL
The ConnectionPool
is initialized with a URL
object, which specifies the database connection details:
zdb::URL url(
"mysql://localhost:3306/mydb?user=root&password=secret");
pool.start();
Represents a database connection pool.
Definition zdbpp.h:1740
Represents an immutable Uniform Resource Locator.
Definition zdbpp.h:334
A ConnectionPool is neither copyable or movable. It is designed to be a long-lived object that manages database connections throughout the lifetime of your application. Typically, you would instantiate one or more ConnectionPool objects as part of a resource management class or in the global scope of your application.
Best Practices for Using ConnectionPool
- Create ConnectionPool instances at application startup.
- Maintain these instances for the entire duration of your application's runtime.
- Use a single ConnectionPool for each distinct database you need to connect to.
- Consider wrapping ConnectionPool instances in a singleton or dependency injection pattern for easy access across your application.
- Ensure proper shutdown of ConnectionPool instances when your application terminates to release all database resources cleanly.
Example of a global ConnectionPool manager:
class DatabaseManager {
public:
static ConnectionPool& getMainPool() {
static ConnectionPool mainPool("mysql://localhost/maindb?user=root&password=pass");
return mainPool;
}
static ConnectionPool& getAnalyticsPool() {
static ConnectionPool analyticsPool("postgresql://analyst:pass@192.168.8.217/datawarehouse");
return analyticsPool;
}
static void initialize() {
static std::once_flag initFlag;
std::call_once(initFlag, []() {
ConnectionPool& main = getMainPool();
main.setInitialConnections(5);
main.setMaxConnections(20);
main.setConnectionTimeout(30);
main.start();
ConnectionPool& analytics = getAnalyticsPool();
analytics.setInitialConnections(2);
analytics.setMaxConnections(10);
analytics.start();
});
}
static void shutdown() {
getMainPool().stop();
getAnalyticsPool().stop();
}
};
Usage Examples
Basic Query Execution
auto& pool = DatabaseManager::getMainPool();
auto con = pool.getConnection();
ResultSet result = con.executeQuery("SELECT name, age FROM users WHERE id = ?", 1);
if (result.next()) {
std::cout << "Name: " << result.getString("name").value_or("N/A")
<< ", Age: " << result.getInt("age") << std::endl;
}
Using PreparedStatement
auto& pool = DatabaseManager::getAnalyticsPool();
auto con = pool.getConnection();
auto stmt = con.prepareStatement("INSERT INTO logs (message, timestamp) VALUES (?, ?)");
stmt.bindValues("User logged in", std::time(nullptr));
stmt.execute();
Transaction Example
If an exception occurs at any point before commit() is called, the transaction will be automatically rolled back when the Connection object goes out of scope and is returned to the pool. This ensures data integrity even in the face of exceptions.
try {
Connection con = pool.getConnection();
con.beginTransaction();
con.execute("UPDATE accounts SET balance = balance - ? WHERE id = ?", 100.0, 1);
con.execute("UPDATE accounts SET balance = balance + ? WHERE id = ?", 100.0, 2);
con.commit();
std::cout << "Transfer successful" << std::endl;
} catch (const sql_exception& e) {
std::cerr << "Operation failed: " << e.what() << std::endl;
}
Exception Handling
All database-related errors are thrown as sql_exception
, which derives from std::runtime_error
.
Example of Exception Handling
try {
auto con = pool.getConnection();
con.execute("INSERT INTO users (name, email) VALUES (?, ?)", "John Doe", "john@example.com");
std::cerr << "Database error occurred: " << e.what() << std::endl;
}
Exception class for SQL related errors.
Definition zdbpp.h:276
Key points about exception handling in this library:
- All database operations that can fail will throw
sql_exception
.
sql_exception
provides informative error messages through its what()
method.
- You should wrap database operations in try-catch blocks to handle potential errors gracefully.
- The library ensures that resources are properly managed even when exceptions are thrown, preventing resource leaks.
- Note
- For detailed API documentation, refer to the comments for each class in this header file. Visit libzdb's homepage for additional documentation and examples.