1 /**
2  * HibernateD - Object-Relation Mapping for D programming language, with interface similar to Hibernate. 
3  * 
4  * Hibernate documentation can be found here:
5  * $(LINK http://hibernate.org/docs)$(BR)
6  * 
7  * Source file hibernated/tests.d.
8  *
9  * This module contains unit tests for functional testing on real DB.
10  * 
11  * Copyright: Copyright 2013
12  * License:   $(LINK www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
13  * Author:   Vadim Lopatin
14  */
15 module hibernated.tests;
16 
17 import std.algorithm;
18 import std.conv;
19 import std.stdio;
20 import std.datetime;
21 import std.typecons;
22 import std.exception;
23 import std.variant;
24 
25 import hibernated.core;
26 
27 version(unittest) {
28     
29     //@Entity
30     @Table("users") // to override table name - "users" instead of default "user"
31     class User {
32         
33         //@Generated
34         long id;
35         
36         string name;
37         
38         // property column
39         private long _flags;
40         @Null // override NotNull which is inferred from long type
41         @property void flags(long v) { _flags = v; }
42         @property ref long flags() { return _flags; }
43 
44         // getter/setter property
45         string comment;
46         // @Column -- not mandatory, will be deduced
47         @Null // override default nullability of string with @Null (instead of using String)
48         @Column(null, 1024) // override default length, autogenerate column name)
49         string getComment() { return comment; }
50         void setComment(string v) { comment = v; }
51         
52         //@ManyToOne -- not mandatory, will be deduced
53         //@JoinColumn("customer_fk")
54         Customer customer;
55         
56         @ManyToMany
57         LazyCollection!Role roles;
58         
59         override string toString() {
60             return "id=" ~ to!string(id) ~ ", name=" ~ name ~ ", flags=" ~ to!string(flags) ~ ", comment=" ~ comment ~ ", customerId=" ~ (customer is null ? "NULL" : customer.toString());
61         }
62         
63     }
64     
65     
66     //@Entity
67     @Table("customers") // to override table name - "customers" instead of default "customer"
68     class Customer {
69         //@Generated
70         int id;
71         // @Column -- not mandatory, will be deduced
72         string name;
73 
74         // deduced as @Embedded automatically
75         Address address;
76         
77         //@ManyToOne -- not mandatory, will be deduced
78         //@JoinColumn("account_type_fk")
79         Lazy!AccountType accountType;
80         
81         //        @OneToMany("customer")
82         //        LazyCollection!User users;
83         
84         //@OneToMany("customer") -- not mandatory, will be deduced
85         private User[] _users;
86         @property User[] users() { return _users; }
87         @property void users(User[] value) { _users = value; }
88         
89         this() {
90             address = new Address();
91         }
92         override string toString() {
93             return "id=" ~ to!string(id) ~ ", name=" ~ name ~ ", address=" ~ address.toString();
94         }
95     }
96 
97     static assert(isEmbeddedObjectMember!(Customer, "address"));
98     
99     @Embeddable
100     class Address {
101         hibernated.type.String zip;
102         hibernated.type.String city;
103         hibernated.type.String streetAddress;
104         @Transient // mark field with @Transient to avoid creating column for it
105         string someNonPersistentField;
106 
107         override string toString() {
108             return " zip=" ~ zip ~ ", city=" ~ city ~ ", streetAddress=" ~ streetAddress;
109         }
110     }
111     
112     @Entity // need to have at least one annotation to import automatically from module
113     class AccountType {
114         //@Generated
115         int id;
116         string name;
117     }
118     
119     //@Entity 
120     class Role {
121         //@Generated
122         int id;
123         string name;
124         @ManyToMany 
125         LazyCollection!User users;
126     }
127     
128     //@Entity
129     //@Table("t1")
130     class T1 {
131         //@Id 
132         //@Generated
133         int id;
134         
135         //@NotNull 
136         @UniqueKey
137         string name;
138         
139         // property column
140         private long _flags;
141         // @Column -- not mandatory, will be deduced
142         @property long flags() { return _flags; }
143         @property void flags(long v) { _flags = v; }
144         
145         // getter/setter property
146         private string comment;
147         // @Column -- not mandatory, will be deduced
148         @Null
149         string getComment() { return comment; }
150         void setComment(string v) { comment = v; }
151         
152         
153         override string toString() {
154             return "id=" ~ to!string(id) ~ ", name=" ~ name ~ ", flags=" ~ to!string(flags) ~ ", comment=" ~ comment;
155         }
156     }
157     
158     @Entity
159     static class GeneratorTest {
160         //@Generator("std.uuid.randomUUID().toString()")
161         @Generator(UUID_GENERATOR)
162         string id;
163         string name;
164     }
165     
166     @Entity
167     static class TypeTest {
168         //@Generated
169         int id;
170         string string_field;
171         hibernated.type.String nullable_string_field;
172         byte byte_field;
173         short short_field;
174         int int_field;
175         long long_field;
176         ubyte ubyte_field;
177         ushort ushort_field;
178         ulong ulong_field;
179         DateTime datetime_field;
180         Date date_field;
181         TimeOfDay time_field;
182         Byte nullable_byte_field;
183         Short nullable_short_field;
184         Int nullable_int_field;
185         Long nullable_long_field;
186         Ubyte nullable_ubyte_field;
187         Ushort nullable_ushort_field;
188         Ulong nullable_ulong_field;
189         NullableDateTime nullable_datetime_field;
190         NullableDate nullable_date_field;
191         NullableTimeOfDay nullable_time_field;
192         float float_field;
193         double double_field;
194         Float nullable_float_field;
195         Double nullable_double_field;
196         byte[] byte_array_field;
197         ubyte[] ubyte_array_field;
198     }
199     
200     import ddbc.drivers.mysqlddbc;
201     import ddbc.drivers.pgsqlddbc;
202     import ddbc.drivers.sqliteddbc;
203     import ddbc.common;
204     import hibernated.dialects.mysqldialect;
205     import hibernated.dialects.sqlitedialect;
206     import hibernated.dialects.pgsqldialect;
207 
208     
209     string[] UNIT_TEST_DROP_TABLES_SCRIPT = 
210         [
211          "DROP TABLE IF EXISTS role_users",
212          "DROP TABLE IF EXISTS account_type",
213          "DROP TABLE IF EXISTS users",
214          "DROP TABLE IF EXISTS customers",
215          "DROP TABLE IF EXISTS person",
216          "DROP TABLE IF EXISTS person_info",
217          "DROP TABLE IF EXISTS person_info2",
218          "DROP TABLE IF EXISTS role",
219          "DROP TABLE IF EXISTS generator_test",
220          ];
221     string[] UNIT_TEST_CREATE_TABLES_SCRIPT = 
222         [
223          "CREATE TABLE IF NOT EXISTS role (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL)",
224          "CREATE TABLE IF NOT EXISTS account_type (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL)",
225          "CREATE TABLE IF NOT EXISTS users (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, flags INT, comment TEXT, customer_fk BIGINT NULL)",
226          "CREATE TABLE IF NOT EXISTS customers (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, zip varchar(20), city varchar(100), street_address varchar(255), account_type_fk int)",
227          "CREATE TABLE IF NOT EXISTS person_info (id int not null primary key AUTO_INCREMENT, flags bigint)",
228          "CREATE TABLE IF NOT EXISTS person (id int not null primary key AUTO_INCREMENT, first_name varchar(255) not null, last_name varchar(255) not null, more_info_fk int)",
229          "CREATE TABLE IF NOT EXISTS person_info2 (id int not null primary key AUTO_INCREMENT, flags bigint, person_info_fk int)",
230          "CREATE TABLE IF NOT EXISTS role_users (role_fk int not null, user_fk int not null, primary key (role_fk, user_fk), unique index(user_fk, role_fk))",
231          "CREATE TABLE IF NOT EXISTS generator_test (id varchar(64) not null primary key, name varchar(255) not null)",
232          ];
233     string[] UNIT_TEST_FILL_TABLES_SCRIPT = 
234         [
235          "INSERT INTO role (name) VALUES ('admin')",
236          "INSERT INTO role (name) VALUES ('viewer')",
237          "INSERT INTO role (name) VALUES ('editor')",
238          "INSERT INTO account_type (name) VALUES ('Type1')",
239          "INSERT INTO account_type (name) VALUES ('Type2')",
240          "INSERT INTO customers (name, zip, account_type_fk) VALUES ('customer 1', '12345', 1)",
241          "INSERT INTO customers (name, zip) VALUES ('customer 2', '54321')",
242          "INSERT INTO customers (name, street_address, account_type_fk) VALUES ('customer 3', 'Baker Street, 24', 2)",
243          "INSERT INTO users (name, flags, comment, customer_fk) VALUES ('user 1', 11, 'comments for user 1', 1)",
244          "INSERT INTO users (name, flags, comment, customer_fk) VALUES ('user 2', 22, 'this user belongs to customer 1', 1)",
245          "INSERT INTO users (name, flags, comment, customer_fk) VALUES ('user 3', NULL, 'this user belongs to customer 2', 2)",
246          "INSERT INTO users (name, flags, comment, customer_fk) VALUES ('user 4', 44,   NULL, 3)",
247          "INSERT INTO users (name, flags, comment, customer_fk) VALUES ('test user 5', 55, 'this user belongs to customer 3, too', 3)",
248          "INSERT INTO users (name, flags, comment, customer_fk) VALUES ('test user 6', 66, 'for checking of Nullable!long reading', null)",
249          "INSERT INTO person_info (id, flags) VALUES (3, 123)",
250          "INSERT INTO person_info (id, flags) VALUES (4, 234)",
251          "INSERT INTO person_info (id, flags) VALUES (5, 345)",
252          "INSERT INTO person_info2 (id, flags, person_info_fk) VALUES (10, 1, 3)",
253          "INSERT INTO person_info2 (id, flags, person_info_fk) VALUES (11, 2, 4)",
254          "INSERT INTO person (first_name, last_name, more_info_fk) VALUES ('Andrei', 'Alexandrescu', 3)",
255          "INSERT INTO person (first_name, last_name, more_info_fk) VALUES ('Walter', 'Bright', 4)",
256          "INSERT INTO person (first_name, last_name, more_info_fk) VALUES ('John', 'Smith', 5)",
257          "INSERT INTO role_users (role_fk, user_fk) VALUES (1, 1)",
258          "INSERT INTO role_users (role_fk, user_fk) VALUES (2, 1)",
259          "INSERT INTO role_users (role_fk, user_fk) VALUES (2, 2)",
260          "INSERT INTO role_users (role_fk, user_fk) VALUES (2, 3)",
261          "INSERT INTO role_users (role_fk, user_fk) VALUES (3, 2)",
262          "INSERT INTO role_users (role_fk, user_fk) VALUES (3, 3)",
263          "INSERT INTO role_users (role_fk, user_fk) VALUES (3, 5)",
264          ];
265 
266     void recreateTestSchema(bool dropTables, bool createTables, bool fillTables) {
267         DataSource connectionPool = getUnitTestDataSource();
268         if (connectionPool is null)
269             return; // DB tests disabled
270         Connection conn = connectionPool.getConnection();
271         scope(exit) conn.close();
272         if (dropTables)
273             unitTestExecuteBatch(conn, UNIT_TEST_DROP_TABLES_SCRIPT);
274         if (createTables)
275             unitTestExecuteBatch(conn, UNIT_TEST_CREATE_TABLES_SCRIPT);
276         if (fillTables)
277             unitTestExecuteBatch(conn, UNIT_TEST_FILL_TABLES_SCRIPT);
278     }
279 
280     immutable bool DB_TESTS_ENABLED = SQLITE_TESTS_ENABLED || MYSQL_TESTS_ENABLED || PGSQL_TESTS_ENABLED;
281 
282 
283     package DataSource _unitTestConnectionPool;
284     /// will return null if DB tests are disabled
285     DataSource getUnitTestDataSource() {
286         if (_unitTestConnectionPool is null) {
287             static if (SQLITE_TESTS_ENABLED) {
288                 pragma(msg, "Will use SQLite for HibernateD unit tests");
289                 _unitTestConnectionPool = createUnitTestSQLITEDataSource();
290             } else if (MYSQL_TESTS_ENABLED) {
291                 pragma(msg, "Will use MySQL for HibernateD unit tests");
292                 _unitTestConnectionPool = createUnitTestMySQLDataSource();
293             } else if (PGSQL_TESTS_ENABLED) {
294                 pragma(msg, "Will use PGSQL for HibernateD unit tests");
295                 _unitTestConnectionPool = createUnitTestPGSQLDataSource();
296             }
297         }
298         return _unitTestConnectionPool;
299     }
300     Dialect getUnitTestDialect() {
301 
302         static if (SQLITE_TESTS_ENABLED) {
303             return new SQLiteDialect();
304         } else if (MYSQL_TESTS_ENABLED) {
305             return new MySQLDialect();
306         } else if (PGSQL_TESTS_ENABLED) {
307             return new PGSQLDialect();
308         } else {
309             return null; // disabled
310         }
311     }
312 
313     void closeUnitTestDataSource() {
314 //        if (!_unitTestConnectionPool is null) {
315 //            _unitTestConnectionPool.close();
316 //            _unitTestConnectionPool = null;
317 //        }
318     }
319 }
320 
321 
322 unittest {
323     
324     // Checking generated metadata
325     EntityMetaData schema = new SchemaInfoImpl!(User, Customer, AccountType, T1, TypeTest, Address, Role);
326     
327     //writeln("metadata test 1");
328     assert(schema["TypeTest"].length==29);
329     assert(schema.getEntityCount() == 7);
330     assert(schema["User"]["name"].columnName == "name");
331     assert(schema["User"][0].columnName == "id");
332     assert(schema["User"][2].propertyName == "flags");
333     assert(schema["User"]["id"].generated == true);
334     assert(schema["User"]["id"].key == true);
335     assert(schema["User"]["name"].key == false);
336     assert(schema["Customer"]["id"].generated == true);
337     assert(schema["Customer"]["id"].key == true);
338     assert(schema["Customer"]["address"].embedded == true);
339     assert(schema["Customer"]["address"].referencedEntity !is null);
340     assert(schema["Customer"]["address"].referencedEntity["streetAddress"].columnName == "street_address");
341     assert(schema["User"]["customer"].columnName !is null);
342 
343     assert(!schema["User"].embeddable); // test if @Embeddable is working
344     assert(schema["Address"].embeddable); // test if @Embeddable is working
345     assert(schema["Address"].length == 3); // test if @Transient is working
346 
347     assert(schema["Customer"]["users"].relation == RelationType.OneToMany);
348     assert(schema["User"]["customer"].relation == RelationType.ManyToOne);
349     assert(schema["User"]["roles"].relation == RelationType.ManyToMany);
350     assert(schema["User"]["roles"].joinTable !is null);
351     assert(schema["User"]["roles"].joinTable.tableName == "role_users");
352     assert(schema["User"]["roles"].joinTable.column1 == "user_fk");
353     assert(schema["User"]["roles"].joinTable.column2 == "role_fk");
354     assert(schema["Role"]["users"].joinTable.tableName == "role_users");
355     assert(schema["Role"]["users"].joinTable.column1 == "role_fk");
356     assert(schema["Role"]["users"].joinTable.column2 == "user_fk");
357 
358     assert(schema["Customer"]["users"].collection);
359 
360     assert(schema["User"]["id"].readFunc !is null);
361 
362     assert(schema["User"]["comment"].length == 1024);
363     static assert(isGetterFunction!(__traits(getMember, User, "getComment"), "getComment"));
364     static assert(isGetterFunction!(__traits(getMember, T1, "getComment"), "getComment"));
365     static assert(hasMemberAnnotation!(User, "getComment", Null));
366     static assert(hasMemberAnnotation!(T1, "getComment", Null));
367     static assert(!isMainMemberForProperty!(User, "comment"));
368     static assert(isMainMemberForProperty!(User, "getComment"));
369     static assert(isMainMemberForProperty!(Customer, "users"));
370 
371     assert(schema["T1"]["comment"].nullable);
372     assert(schema["User"]["comment"].nullable);
373 
374     assert(schema["TypeTest"]["id"].key);
375     assert(schema["TypeTest"]["id"].key);
376     assert(!schema["TypeTest"]["string_field"].nullable);
377     assert(schema["TypeTest"]["nullable_string_field"].nullable);
378     assert(!schema["TypeTest"]["byte_field"].nullable);
379     assert(!schema["TypeTest"]["short_field"].nullable);
380     assert(!schema["TypeTest"]["int_field"].nullable);
381     assert(!schema["TypeTest"]["long_field"].nullable);
382     assert(!schema["TypeTest"]["ubyte_field"].nullable);
383     assert(!schema["TypeTest"]["ushort_field"].nullable);
384     assert(!schema["TypeTest"]["ulong_field"].nullable);
385     assert(!schema["TypeTest"]["datetime_field"].nullable);
386     assert(!schema["TypeTest"]["date_field"].nullable);
387     assert(!schema["TypeTest"]["time_field"].nullable);
388     assert(schema["TypeTest"]["nullable_byte_field"].nullable);
389     assert(schema["TypeTest"]["nullable_short_field"].nullable);
390     assert(schema["TypeTest"]["nullable_int_field"].nullable);
391     assert(schema["TypeTest"]["nullable_long_field"].nullable);
392     assert(schema["TypeTest"]["nullable_ubyte_field"].nullable);
393     assert(schema["TypeTest"]["nullable_ushort_field"].nullable);
394     assert(schema["TypeTest"]["nullable_ulong_field"].nullable);
395     assert(schema["TypeTest"]["nullable_datetime_field"].nullable);
396     assert(schema["TypeTest"]["nullable_date_field"].nullable);
397     assert(schema["TypeTest"]["nullable_time_field"].nullable);
398     assert(!schema["TypeTest"]["float_field"].nullable);
399     assert(!schema["TypeTest"]["double_field"].nullable);
400     assert(schema["TypeTest"]["nullable_float_field"].nullable);
401     assert(schema["TypeTest"]["nullable_double_field"].nullable);
402     assert(schema["TypeTest"]["byte_array_field"].nullable);
403     assert(schema["TypeTest"]["ubyte_array_field"].nullable);
404 
405     auto e2 = schema.createEntity("User");
406     assert(e2 !is null);
407     User e2user = cast(User)e2;
408     assert(e2user !is null);
409 
410 
411 
412 
413     // TODO:
414     //    e2user.customer = new Customer();
415     //    e2user.customer.id = 25;
416     //    e2user.customer.name = "cust25";
417     //    Variant v = schema.getPropertyValue(e2user, "customer");
418     //    assert(v.get!Customer().name == "cust25");
419     //    e2user.customer = null;
420     //    //assert(schema.getPropertyValue(e2user, "customer").to!Object is null); //Variant(cast(Customer)null));
421     //    Customer c42 = new Customer();
422     //    c42.id = 42;
423     //    c42.name = "customer 42";
424     //    schema.setPropertyValue(e2user, "customer", Variant(c42));
425     //    assert(e2user.customer.id == 42);
426     //    //assert(schema.getPropertyValue(e2user, "customer") == 42);
427     
428     Object e1 = schema.findEntity("User").createEntity();
429     assert(e1 !is null);
430     User e1user = cast(User)e1;
431     assert(e1user !is null);
432     e1user.id = 25;
433     
434     
435     
436 }
437 
438 unittest {
439     if (DB_TESTS_ENABLED) {
440         recreateTestSchema(true, false, false);
441 
442 
443         //writeln("metadata test 2");
444         
445         // Checking generated metadata
446         EntityMetaData schema = new SchemaInfoImpl!(User, Customer, AccountType, T1, TypeTest, Address, Role, GeneratorTest, Person, MoreInfo, EvenMoreInfo);
447         Dialect dialect = getUnitTestDialect();
448 
449         DBInfo db = new DBInfo(dialect, schema);
450         string[] createTables = db.getCreateTableSQL();
451 //        foreach(t; createTables)
452 //            writeln(t);
453         string[] createIndexes = db.getCreateIndexSQL();
454 //        foreach(t; createIndexes)
455 //            writeln(t);
456         static if (SQLITE_TESTS_ENABLED) {
457             assert(db["users"].getCreateTableSQL() == "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, flags INT NULL, comment TEXT NULL, customer_fk INT NULL)");
458             assert(db["customers"].getCreateTableSQL() == "CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT NOT NULL, zip TEXT NULL, city TEXT NULL, street_address TEXT NULL, account_type_fk INT NULL)");
459             assert(db["account_type"].getCreateTableSQL() == "CREATE TABLE account_type (id INTEGER PRIMARY KEY, name TEXT NOT NULL)");
460             assert(db["t1"].getCreateTableSQL() == "CREATE TABLE t1 (id INTEGER PRIMARY KEY, name TEXT NOT NULL, flags INT NOT NULL, comment TEXT NULL)");
461             assert(db["role"].getCreateTableSQL() == "CREATE TABLE role (id INTEGER PRIMARY KEY, name TEXT NOT NULL)");
462             assert(db["generator_test"].getCreateTableSQL() == "CREATE TABLE generator_test (id TEXT NOT NULL PRIMARY KEY, name TEXT NOT NULL)");
463             assert(db["role_users"].getCreateTableSQL() == "CREATE TABLE role_users (role_fk INT NOT NULL, user_fk INT NOT NULL, PRIMARY KEY (role_fk, user_fk), UNIQUE (user_fk, role_fk))");
464         } else static if (MYSQL_TESTS_ENABLED) {
465             assert(db["users"].getCreateTableSQL() == "CREATE TABLE users (id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, flags BIGINT NULL, comment VARCHAR(1024) NULL, customer_fk INT NULL)");
466             assert(db["customers"].getCreateTableSQL() == "CREATE TABLE customers (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, zip VARCHAR(255) NULL, city VARCHAR(255) NULL, street_address VARCHAR(255) NULL, account_type_fk INT NULL)");
467             assert(db["account_type"].getCreateTableSQL() == "CREATE TABLE account_type (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL)");
468             assert(db["t1"].getCreateTableSQL() == "CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, flags BIGINT NOT NULL, comment VARCHAR(255) NULL)");
469             assert(db["role"].getCreateTableSQL() == "CREATE TABLE role (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL)");
470             assert(db["generator_test"].getCreateTableSQL() == "CREATE TABLE generator_test (id VARCHAR(255) NOT NULL PRIMARY KEY, name VARCHAR(255) NOT NULL)");
471             assert(db["role_users"].getCreateTableSQL() == "CREATE TABLE role_users (role_fk INT NOT NULL, user_fk BIGINT NOT NULL, PRIMARY KEY (role_fk, user_fk), UNIQUE INDEX role_users_reverse_index (user_fk, role_fk))");
472         } else static if (PGSQL_TESTS_ENABLED) {
473         }
474 
475 
476 
477         DataSource ds = getUnitTestDataSource();
478         if (ds is null)
479             return; // DB tests disabled
480         SessionFactory factory = new SessionFactoryImpl(schema, dialect, ds);
481         db = factory.getDBMetaData();
482         {
483             Connection conn = ds.getConnection();
484             scope(exit) conn.close();
485             db.updateDBSchema(conn, true, true);
486             recreateTestSchema(false, false, true);
487         }
488 
489 
490 
491 
492         scope(exit) factory.close();
493         {
494             Session sess = factory.openSession();
495             scope(exit) sess.close();
496             
497             User u1 = sess.load!User(1);
498             //writeln("Loaded value: " ~ u1.toString);
499             assert(u1.id == 1);
500             assert(u1.name == "user 1");
501             assert(u1.customer.name == "customer 1");
502             assert(u1.customer.accountType() !is null);
503             assert(u1.customer.accountType().name == "Type1");
504             Role[] u1roles = u1.roles;
505             assert(u1roles.length == 2);
506             
507             User u2 = sess.load!User(2);
508             assert(u2.name == "user 2");
509             assert(u2.flags == 22); // NULL is loaded as 0 if property cannot hold nulls
510             
511             User u3 = sess.get!User(3);
512             assert(u3.name == "user 3");
513             assert(u3.flags == 0); // NULL is loaded as 0 if property cannot hold nulls
514             assert(u3.getComment() !is null);
515             assert(u3.customer.name == "customer 2");
516             assert(u3.customer.accountType() is null);
517             
518             User u4 = new User();
519             sess.load(u4, 4);
520             assert(u4.name == "user 4");
521             assert(u4.getComment() is null);
522             
523             User u5 = new User();
524             u5.id = 5;
525             sess.refresh(u5);
526             assert(u5.name == "test user 5");
527             //assert(u5.customer !is null);
528             
529             u5 = sess.load!User(5);
530             assert(u5.name == "test user 5");
531             assert(u5.customer !is null);
532             assert(u5.customer.id == 3);
533             assert(u5.customer.name == "customer 3");
534             assert(u5.customer.accountType() !is null);
535             assert(u5.customer.accountType().name == "Type2");
536             
537             User u6 = sess.load!User(6);
538             assert(u6.name == "test user 6");
539             assert(u6.customer is null);
540             
541             // 
542             //writeln("loading customer 3");
543             // testing @Embedded property
544             Customer c3 = sess.load!Customer(3);
545             assert(c3.address.zip is null);
546             assert(c3.address.streetAddress == "Baker Street, 24");
547             c3.address.streetAddress = "Baker Street, 24/2";
548             c3.address.zip = "55555";
549             
550             User[] c3users = c3.users;
551             //writeln("        ***      customer has " ~ to!string(c3users.length) ~ " users");
552             assert(c3users.length == 2);
553             assert(c3users[0].customer == c3);
554             assert(c3users[1].customer == c3);
555             
556             //writeln("updating customer 3");
557             sess.update(c3);
558             Customer c3_reloaded = sess.load!Customer(3);
559             assert(c3.address.streetAddress == "Baker Street, 24/2");
560             assert(c3.address.zip == "55555");
561 
562         }
563         {
564             Session sess = factory.openSession();
565             scope(exit) sess.close();
566 
567             
568             // check Session.save() when id is filled
569             Customer c4 = new Customer();
570             c4.id = 4;
571             c4.name = "Customer_4";
572             sess.save(c4);
573             
574             Customer c4_check = sess.load!Customer(4);
575             assert(c4.id == c4_check.id);
576             assert(c4.name == c4_check.name);
577             
578             sess.remove(c4);
579             
580             c4 = sess.get!Customer(4);
581             assert (c4 is null);
582             
583             Customer c5 = new Customer();
584             c5.name = "Customer_5";
585             sess.save(c5);
586             
587             // Testing generator function (uuid)
588             GeneratorTest g1 = new GeneratorTest();
589             g1.name = "row 1";
590             assert(g1.id is null);
591             sess.save(g1);
592             assert(g1.id !is null);
593             
594             
595             assertThrown!MappingException(sess.createQuery("SELECT id, name, blabla FROM User ORDER BY name"));
596             assertThrown!QuerySyntaxException(sess.createQuery("SELECT id: name FROM User ORDER BY name"));
597             
598             // test multiple row query
599             Query q = sess.createQuery("FROM User ORDER BY name");
600             User[] list = q.list!User();
601             assert(list.length == 6);
602             assert(list[0].name == "test user 5");
603             assert(list[1].name == "test user 6");
604             assert(list[2].name == "user 1");
605             //      writeln("Read " ~ to!string(list.length) ~ " rows from User");
606             //      foreach(row; list) {
607             //          writeln(row.toString());
608             //      }
609             Variant[][] rows = q.listRows();
610             assert(rows.length == 6);
611             //      foreach(row; rows) {
612             //          writeln(row);
613             //      }
614             assertThrown!HibernatedException(q.uniqueResult!User());
615             assertThrown!HibernatedException(q.uniqueRow());
616             
617         }
618         {
619             Session sess = factory.openSession();
620             scope(exit) sess.close();
621 
622             // test single row select
623             Query q = sess.createQuery("FROM User AS u WHERE id = :Id and (u.name like '%test%' or flags=44)");
624             assertThrown!HibernatedException(q.list!User()); // cannot execute w/o all parameters set
625             q.setParameter("Id", Variant(6));
626             User[] list = q.list!User();
627             assert(list.length == 1);
628             assert(list[0].name == "test user 6");
629             //      writeln("Read " ~ to!string(list.length) ~ " rows from User");
630             //      foreach(row; list) {
631             //          writeln(row.toString());
632             //      }
633             User uu = q.uniqueResult!User();
634             assert(uu.name == "test user 6");
635             Variant[] row = q.uniqueRow();
636             assert(row[0] == 6L);
637             assert(row[1] == "test user 6");
638             
639             // test empty SELECT result
640             q.setParameter("Id", Variant(7));
641             row = q.uniqueRow();
642             assert(row is null);
643             uu = q.uniqueResult!User();
644             assert(uu is null);
645             
646             q = sess.createQuery("SELECT c.name, c.address.zip FROM Customer AS c WHERE id = :Id").setParameter("Id", Variant(1));
647             row = q.uniqueRow();
648             assert(row !is null);
649             assert(row[0] == "customer 1"); // name
650             assert(row[1] == "12345"); // address.zip
651 
652 
653         }
654         {
655             // prepare data
656             Session sess = factory.openSession();
657             scope(exit) sess.close();
658 
659             Role r10 = new Role();
660             r10.name = "role10";
661             Role r11 = new Role();
662             r11.name = "role11";
663             Customer c10 = new Customer();
664             c10.name = "Customer 10";
665             User u10 = new User();
666             u10.name = "Alex";
667             u10.customer = c10;
668             u10.roles = [r10, r11];
669             sess.save(r10);
670             sess.save(r11);
671             sess.save(c10);
672             sess.save(u10);
673             assert(c10.id != 0);
674             assert(u10.id != 0);
675             assert(r10.id != 0);
676             assert(r11.id != 0);
677         }
678         {
679             // check data in separate session
680             Session sess = factory.openSession();
681             scope(exit) sess.close();
682             User u10 = sess.createQuery("FROM User WHERE name=:Name").setParameter("Name", "Alex").uniqueResult!User();
683             assert(u10.roles.length == 2);
684             assert(u10.roles[0].name == "role10" || u10.roles.get()[0].name == "role11");
685             assert(u10.roles[1].name == "role10" || u10.roles.get()[1].name == "role11");
686             assert(u10.customer.name == "Customer 10");
687             assert(u10.customer.users.length == 1);
688             assert(u10.customer.users[0] == u10);
689             assert(u10.roles[0].users.length == 1);
690             assert(u10.roles[0].users[0] == u10);
691             // removing one role from user
692             u10.roles.get().remove(0);
693             sess.update(u10);
694         }
695         {
696             // check that only one role left
697             Session sess = factory.openSession();
698             scope(exit) sess.close();
699             User u10 = sess.createQuery("FROM User WHERE name=:Name").setParameter("Name", "Alex").uniqueResult!User();
700             assert(u10.roles.length == 1);
701             assert(u10.roles[0].name == "role10" || u10.roles.get()[0].name == "role11");
702             // remove user
703             sess.remove(u10);
704         }
705         {
706             // check that user is removed
707             Session sess = factory.openSession();
708             scope(exit) sess.close();
709             User u10 = sess.createQuery("FROM User WHERE name=:Name").setParameter("Name", "Alex").uniqueResult!User();
710             assert(u10 is null);
711         }
712     }
713 }
714 
715 
716 version (unittest) {
717     // for testing of Embeddable
718     @Embeddable 
719     class EMName {
720         string firstName;
721         string lastName;
722     }
723     
724     //@Entity 
725     class EMUser {
726         //@Id @Generated
727         //@Column
728         int id;
729         
730         // deduced as @Embedded automatically
731         EMName userName;
732     }
733     
734     // for testing of Embeddable
735     //@Entity
736     class Person {
737         //@Id
738         int id;
739         
740         // @Column @NotNull        
741         string firstName;
742         // @Column @NotNull        
743         string lastName;
744         
745         @NotNull
746         @OneToOne
747         @JoinColumn("more_info_fk")
748         MoreInfo moreInfo;
749     }
750     
751     
752     //@Entity
753     @Table("person_info")
754     class MoreInfo {
755         //@Id @Generated
756         int id;
757         // @Column 
758         long flags;
759         @OneToOne("moreInfo")
760         Person person;
761         @OneToOne("personInfo")
762         EvenMoreInfo evenMore;
763     }
764     
765     //@Entity
766     @Table("person_info2")
767     class EvenMoreInfo {
768         //@Id @Generated
769         int id;
770         //@Column 
771         long flags;
772         @OneToOne
773         @JoinColumn("person_info_fk")
774         MoreInfo personInfo;
775     }
776     
777 }
778 
779 
780 unittest {
781     static assert(hasAnnotation!(EMName, Embeddable));
782     static assert(isEmbeddedObjectMember!(EMUser, "userName"));
783     static assert(!hasMemberAnnotation!(EMUser, "userName", OneToOne));
784     static assert(getPropertyEmbeddedEntityName!(EMUser, "userName") == "EMName");
785     static assert(getPropertyEmbeddedClassName!(EMUser, "userName") == "hibernated.tests.EMName");
786     //pragma(msg, getEmbeddedPropertyDef!(EMUser, "userName")());
787     
788     // Checking generated metadata
789     EntityMetaData schema = new SchemaInfoImpl!(EMName, EMUser);
790     
791     static assert(hasMemberAnnotation!(Person, "moreInfo", OneToOne));
792     static assert(getPropertyReferencedEntityName!(Person, "moreInfo") == "MoreInfo");
793     static assert(getPropertyReferencedClassName!(Person, "moreInfo") == "hibernated.tests.MoreInfo");
794     //pragma(msg, getOneToOnePropertyDef!(Person, "moreInfo"));
795     //pragma(msg, getOneToOnePropertyDef!(MoreInfo, "person"));
796     //pragma(msg, "running getOneToOneReferencedPropertyName");
797     //pragma(msg, getOneToOneReferencedPropertyName!(MoreInfo, "person"));
798     static assert(getJoinColumnName!(Person, "moreInfo") == "more_info_fk");
799     static assert(getOneToOneReferencedPropertyName!(MoreInfo, "person") == "moreInfo");
800     static assert(getOneToOneReferencedPropertyName!(Person, "moreInfo") is null);
801     
802     //pragma(msg, "done getOneToOneReferencedPropertyName");
803     
804     // Checking generated metadata
805     //EntityMetaData schema = new SchemaInfoImpl!(Person, MoreInfo);
806     //  foreach(e; schema["Person"]) {
807     //      writeln("property: " ~ e.propertyName);
808     //  }
809     schema = new SchemaInfoImpl!(hibernated.tests); //Person, MoreInfo, EvenMoreInfo, 
810     
811     {
812         
813         int[Variant] map0;
814         map0[Variant(1)] = 3;
815         assert(map0[Variant(1)] == 3);
816         map0[Variant(1)]++;
817         assert(map0[Variant(1)] == 4);
818         
819         //writeln("map test");
820         PropertyLoadMap map = new PropertyLoadMap();
821         Person ppp1 = new Person();
822         Person ppp2 = new Person();
823         Person ppp3 = new Person();
824         //writeln("adding first");
825         map.add(schema["Person"]["moreInfo"], Variant(1), ppp1);
826         //writeln("adding second");
827         auto prop1 = schema["Person"]["moreInfo"];
828         auto prop2 = schema["Person"]["moreInfo"];
829         map.add(prop1, Variant(2), ppp2);
830         map.add(prop2, Variant(2), ppp3);
831         map.add(prop2, Variant(2), ppp3);
832         map.add(prop2, Variant(2), ppp3);
833         map.add(prop2, Variant(2), ppp3);
834         assert(prop1 == prop2);
835         assert(prop1.opHash() == prop2.opHash());
836         //writeln("checking length");
837         assert(Variant(3) == Variant(3L));
838         assert(map.length == 1);
839         assert(map.map.length == 1);
840         assert(map.keys.length == 1);
841         assert(map.map.values.length == 1);
842         //writeln("length of moreInfo is " ~ to!string(map[prop1].length));
843         auto m = map[prop1];
844         assert(m == map[prop2]);
845         assert(m.map.length == 2);
846         Variant v1 = 1;
847         Variant v2 = 2;
848         //writeln("length for id 1 " ~ to!string(m[Variant(1)].length));
849         //writeln("length for id 2 " ~ to!string(m[Variant(2)].length));
850         assert(m.length == 2);
851         assert(m[Variant(1)].length == 1);
852         assert(m[Variant(2)].length == 2);
853     }
854     
855     if (DB_TESTS_ENABLED) {
856         //recreateTestSchema();
857         
858         //writeln("metadata test 2");
859         import hibernated.dialects.mysqldialect;
860         
861         // Checking generated metadata
862         Dialect dialect = getUnitTestDialect();
863         DataSource ds = getUnitTestDataSource();
864         if (ds is null)
865             return; // DB tests disabled
866         SessionFactory factory = new SessionFactoryImpl(schema, dialect, ds);
867         scope(exit) factory.close();
868         {
869             Session sess = factory.openSession();
870             scope(exit) sess.close();
871 
872             auto p1 = sess.get!Person(1);
873             assert(p1.firstName == "Andrei");
874             
875 
876             // all non-null oneToOne relations
877             auto q = sess.createQuery("FROM Person WHERE id=:Id").setParameter("Id", Variant(1));
878             Person p2 = q.uniqueResult!Person();
879             assert(p2.firstName == "Andrei");
880             assert(p2.moreInfo !is null);
881             assert(p2.moreInfo.person !is null);
882             assert(p2.moreInfo.person == p2);
883             assert(p2.moreInfo.flags == 123);
884             assert(p2.moreInfo.evenMore !is null);
885             assert(p2.moreInfo.evenMore.flags == 1);
886             assert(p2.moreInfo.evenMore.personInfo !is null);
887             assert(p2.moreInfo.evenMore.personInfo == p2.moreInfo);
888 
889 
890             // null oneToOne relation
891             q = sess.createQuery("FROM Person WHERE id=:Id").setParameter("Id", Variant(3));
892             p2 = q.uniqueResult!Person();
893             assert(p2.firstName == "John");
894             assert(p2.moreInfo !is null);
895             assert(p2.moreInfo.person !is null);
896             assert(p2.moreInfo.person == p2);
897             assert(p2.moreInfo.flags == 345);
898             assert(p2.moreInfo.evenMore is null);
899 
900         }
901         
902     }
903 }
904 
905