1 /*
2  * Entity - Entity is an object-relational mapping tool for the D programming language. Referring to the design idea of JPA.
3  *
4  * Copyright (C) 2015-2018  Shanghai Putao Technology Co., Ltd
5  *
6  * Developer: HuntLabs.cn
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11  
12 module hunt.entity.EntityManagerFactory;
13 
14 import hunt.entity;
15 import hunt.entity.dialect;
16 import hunt.entity.EntityOption;
17 import hunt.logging;
18 import hunt.Exceptions;
19 
20 class EntityManagerFactory {
21 
22     Dialect _dialect;
23     EntityOption _option;
24     Database _db;
25     string _name;
26     private CriteriaBuilder _criteriaBuilder;
27     private string[] _exitTables;
28 
29     this(string name, EntityOption option)
30     {
31         _name = name;
32         _option = option;
33 
34         version(HUNT_ENTITY_DEBUG) {
35             tracef("maxPoolSize: %d", _option.pool.maxPoolSize);
36         }
37         
38         auto databaseOptions = new DatabaseOption(_option.database.url);
39         databaseOptions.maximumPoolSize(_option.pool.maxPoolSize);
40         databaseOptions.minimumPoolSize(_option.pool.minPoolSize);
41         databaseOptions.setConnectionTimeout(_option.pool.connectionTimeout);
42         databaseOptions.setEncoderBufferSize(512);  // The sql string may be very long.
43         databaseOptions.maxWaitQueueSize = _option.pool.maxWaitQueueSize;
44 
45         _db = new Database(databaseOptions);
46         
47         if(databaseOptions.isMysql()) {
48             _dialect = new MySQLDialect();
49         } else {
50             _dialect = new PostgreSQLDialect();
51         }
52 
53         _criteriaBuilder = new CriteriaBuilder(this);
54         _exitTables = showTables();
55         autoCreateTables();
56     }
57 
58     ~this()
59     {
60         // if(_db)
61         //     _db.close();
62         // _db = null;
63     }   
64 
65     EntityManager currentEntityManager()
66     {
67         version(HUNT_DATABASE_DEBUG) warning("Try to get current EntityManager");
68         if(_entityManagerInThread is null) {
69             version(HUNT_DATABASE_DEBUG) warning("A new EntityManager created");
70             _entityManagerInThread = new EntityManager(_criteriaBuilder, _name, _option, _db, _dialect);
71         }
72         return _entityManagerInThread;
73     }
74     private static EntityManager _entityManagerInThread;
75 
76     deprecated("Using currentEntityManager instead.")
77     EntityManager createEntityManager()
78     {
79         return currentEntityManager();
80         // return new EntityManager(_criteriaBuilder, _name, _option, _db, _dialect);
81     }
82 
83     EntityManager newEntityManager()
84     {
85         return new EntityManager(_criteriaBuilder, _name, _option, _db, _dialect);
86     }
87     
88     QueryBuilder createQueryBuilder()
89     {
90         return _db.createQueryBuilder();
91     }
92 
93     void close()
94     {
95         if (_db)
96             _db.close();
97 
98         _db = null;
99     }
100 
101     private string[] showTables() {
102         string[] ret;
103         QueryBuilder builder = createQueryBuilder();
104         RowSet rs = _db.query(builder.showTables().toString());
105         foreach(Row row; rs) {
106             for(int i=0; i<row.size(); i++) {
107                 ret ~= row.getValue(i).toString();
108             }
109         }
110 
111         return ret;
112     }
113 
114     private string[] descTable(string tableName)
115     {
116         string[] ret;
117         QueryBuilder builder = createQueryBuilder();
118         RowSet rs = _db.query(builder.descTable(tableName).toString());
119         foreach(Row row; rs) {
120             // string[string] array = row.toStringArray();
121             // ret ~= "Field" in array ? array["Field"] : array["field"];
122 
123             for(int i=0; i<row.size(); i++) {
124                 ret ~= row.getColumnName(i);
125             }
126 
127             // FIXME: Needing refactor or cleanup -@zxp at 9/18/2019, 2:34:02 PM
128             // 
129             // just one row??
130         }
131         return ret;
132     }
133 
134     static void prepareEntity(T...)() {
135         foreach(V; T) {
136             addCreateTableHandle(getEntityTableName!V, &onCreateTableHandler!V);
137         }
138     }
139 
140     void createTables(T...)() {
141         prepareEntity!(T);
142         autoCreateTables();
143     }
144 
145     void autoCreateTables()
146     {
147         GetCreateTableHandle[string] flushList;
148         foreach(k,v; __createTableList) {
149             string check = _option.database.prefix~k;
150             if (!Common.inArray(_exitTables, _option.database.prefix~k)) {
151                 flushList[k] = v;
152                 trace("create new table ", _option.database.prefix~k);
153             }
154         }
155 
156         string[] alterRows;
157         //step1:create base table
158         foreach(v;flushList) {
159             // FIXME: Needing refactor or cleanup -@zxp at 9/18/2019, 1:36:04 PM
160             // 
161             
162             string createSql = v(_dialect, _option.database.prefix, alterRows);
163             _db.execute(createSql);
164         }
165         
166         //step2: alert table, eg add foreign key..
167         foreach(v; alterRows) {
168             trace(v);
169             _db.execute(v);
170         }
171     }
172 
173     Dialect getDialect() {return _dialect;}
174 
175     Database getDatabase() {return _db;}
176 
177     // CriteriaBuilder getCriteriaBuilder() {return _criteriaBuilder;}
178 }
179 
180 
181 
182 alias GetCreateTableHandle = string function(Dialect dialect, string tablePrefix, ref string[] alterRows);
183 string onCreateTableHandler(T)(Dialect dialect, string tablePrefix, ref string[] alertRows)
184 {
185     return (new EntityCreateTable!T).createTable(dialect, tablePrefix, alertRows);
186 }
187 void addCreateTableHandle(string tableName, GetCreateTableHandle handler)
188 {
189     if (tableName !in __createTableList)
190         __createTableList[tableName] = handler;
191 }
192 GetCreateTableHandle getCreateTableHandle(string tableName)
193 {
194     return __createTableList.get(tableName, null);
195 }
196 string getEntityTableName(T)() {
197     static if (hasUDA!(T, Table)) {
198         return getUDAs!(getSymbolsByUDA!(T,Table)[0], Table)[0].name;
199     }
200     else {
201         return T.stringof;
202     }
203 }
204 private:
205 __gshared GetCreateTableHandle[string] __createTableList;