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