1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
|
// file : view/employee.hxx
// copyright : not copyrighted - public domain
#ifndef EMPLOYEE_HXX
#define EMPLOYEE_HXX
#include <string>
#include <cstddef> // std::size_t
#include <odb/core.hxx>
#include <odb/nullable.hxx>
// Include TR1 <memory> header in a compiler-specific fashion. Fall back
// on the Boost implementation if the compiler does not support TR1.
//
#include <odb/tr1/memory.hxx>
using std::tr1::shared_ptr;
#pragma db object
class country
{
public:
country (const std::string& code, std::string const& name)
: code_ (code), name_ (name)
{
}
const std::string&
name () const
{
return name_;
}
private:
friend class odb::access;
country () {}
#pragma db id
std::string code_; // ISO 2-letter country code.
std::string name_;
};
#pragma db object
class employer
{
public:
employer (unsigned long id, const std::string& name)
: id_ (id), name_ (name)
{
}
const std::string&
name () const
{
return name_;
}
private:
friend class odb::access;
employer () {}
#pragma db id
unsigned long id_;
std::string name_;
};
#pragma db object
class employee
{
public:
employee (unsigned long id,
const std::string& first,
const std::string& last,
unsigned short age,
shared_ptr<country> res,
shared_ptr<country> nat,
shared_ptr<employer> e)
: id_ (id), first_ (first), last_ (last), age_ (age),
residence_ (res),
nationality_ (nat),
employed_by_ (e)
{
}
// Name.
//
const std::string&
first () const
{
return first_;
}
const std::string&
last () const
{
return last_;
}
// Age.
//
unsigned short
age () const
{
return age_;
}
// Employer.
//
shared_ptr<employer>
employed_by () const
{
return employed_by_;
}
// Residence and nationality.
//
shared_ptr<country>
residence () const
{
return residence_;
}
shared_ptr<country>
nationality () const
{
return nationality_;
}
private:
friend class odb::access;
employee () {}
#pragma db id
unsigned long id_;
std::string first_;
std::string last_;
unsigned short age_;
shared_ptr<country> residence_;
shared_ptr<country> nationality_;
shared_ptr<employer> employed_by_;
};
// We also have the "legacy" employee_extra table that is not mapped to any
// persistent class. It has the following columns:
//
// CREATE TABLE employee_extra(
// employee_id INTEGER NOT NULL,
// vacation_days INTEGER NOT NULL,
// previous_employer_id INTEGER)
//
// A simple view with a single associated object. It allows us to get
// the name of an employee without loading any of the other parts, such
// as the referenced country and employer objects. The first and last
// data members in the view are automatically associated to the first_
// and last_ members in the employee object.
//
#pragma db view object(employee)
struct employee_name
{
std::string first;
std::string last;
};
// A simple aggregate view. It allows us to count the number of employees
// matching certain criteria. Here we use a column expression with a
// reference to the id_ data member in the employee object.
//
#pragma db view object(employee)
struct employee_count
{
#pragma db column("count(" + employee::id_ + ")")
std::size_t count;
};
// A simple view with two associated object. It allows us to get the
// name of an employee and its employer without loading any other parts.
// Because there is an unambiguous relationship between the employee and
// employer objects (employee::employed_by_), the ODB compiler is able to
// automatically use this relationship as a join condition. Also, similar
// to the employee_name view, the first and last data members are auto-
// associated to the first_ and last_ members in the employee object.
// For the employer_name member we provide an explicit member reference.
//
#pragma db view object(employee) object(employer)
struct employee_employer
{
std::string first;
std::string last;
#pragma db column(employer::name_)
std::string employer_name;
};
// A more interesting aggregate view that uses the GROUP BY clause. It
// allows us to calculate the min/max ages of employees for each employer.
// Here we use the query condition with a placeholder (?).
//
#pragma db view object(employee) object(employer) \
query ((?) + "GROUP BY" + employer::name_)
struct employer_age
{
#pragma db column(employer::name_)
std::string employer_name;
#pragma db column("min(" + employee::age_ + ")")
unsigned short min_age;
#pragma db column("max(" + employee::age_ + ")")
unsigned short max_age;
};
// A more complex view with three associated objects, two of which are
// of the same type. This requires us to use aliases and disambiguate
// the relationships used to associate each object.
//
#pragma db view object(employee) \
object(country = res_country: employee::residence_) \
object(country = nat_country: employee::nationality_)
struct employee_country
{
std::string first;
std::string last;
#pragma db column(res_country::name_)
std::string res_country_name;
#pragma db column(nat_country::name_)
std::string nat_country_name;
};
// An example of an object loading view. It is a different version of
// the above view that loads the complete objects instead of a subset
// of their data members.
//
#pragma db view object(employee) \
object(country = res: employee::residence_) \
object(country = nat: employee::nationality_)
struct employee_country_objects
{
shared_ptr<employee> e;
shared_ptr<country> res;
shared_ptr<country> nat;
};
// An example of using inner join type. Here we want to find employers
// that have any employees. If we were to use the default left join type,
// then we would have gotten all the employers, regardless of whether
// they have any employees. By using the inner join, we make sure that
// only matching employers are returned.
//
// It is also likely that there will be more than one employee for any
// particular employer which will lead to duplicate employer records
// being returned. To avoid this we use the 'distinct' result modifier.
//
// Try to change the join type or remove 'distinct' to observe the
// change in behavior.
//
#pragma db view object(employer) object(employee inner) query(distinct)
struct employer_with_employees
{
shared_ptr<employer> e;
};
// An example of a native view that provides a complete query and is based
// on an ad-hoc table. This view allows us to load the employee vacation
// information from the legacy employee_extra table.
//
#ifndef ODB_DATABASE_ORACLE
#pragma db view query("SELECT employee_id, vacation_days " \
"FROM view_employee_extra")
#else
#pragma db view query("SELECT \"employee_id\", \"vacation_days\" " \
"FROM \"view_employee_extra\"")
#endif
struct employee_vacation
{
#pragma db type("INTEGER")
unsigned long id;
#pragma db type("INTEGER")
unsigned short days;
};
// A more robust implementation of the above view as a table view instead
// of a native view.
//
#pragma db view table("view_employee_extra")
struct employee_vacation1
{
#pragma db column("employee_id") type("INTEGER")
unsigned long id;
#pragma db column("vacation_days") type("INTEGER")
unsigned short days;
};
// An improved version of the previous view that extracts the employee
// first and last names instead of the id. To get the names we need to
// add the employee object to this view and use a custom join condition
// to tie it up with our legacy table.
//
#ifndef ODB_DATABASE_ORACLE
#pragma db view table("view_employee_extra") \
object(employee: "view_employee_extra.employee_id = " + employee::id_)
#else
#pragma db view table("view_employee_extra") \
object(employee: "\"view_employee_extra\".\"employee_id\" = " + \
employee::id_)
#endif
struct employee_vacation2
{
std::string first;
std::string last;
#pragma db column("view_employee_extra.vacation_days") type("INTEGER")
unsigned short days;
};
// A mixed view that associates two objects and a legacy table. It returns
// the previous employer information for each employee.
//
#ifndef ODB_DATABASE_ORACLE
#pragma db view object(employee) \
table("view_employee_extra" = "extra": \
"extra.employee_id = " + employee::id_) \
object(employer: "extra.previous_employer_id = " + employer::id_)
#else
#pragma db view object(employee) \
table("view_employee_extra" = "extra": \
"\"extra\".\"employee_id\" = " + employee::id_) \
object(employer: "\"extra\".\"previous_employer_id\" = " + employer::id_)
#endif
struct employee_prev_employer
{
std::string first;
std::string last;
// If previous_employer_id is NULL, then the name will be NULL as well.
// We use the odb::nullable wrapper to handle this.
//
#pragma db column(employer::name_)
odb::nullable<std::string> prev_employer_name;
};
#endif // EMPLOYEE_HXX
|