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.EntityManager;
13 
14 import hunt.entity;
15 import hunt.entity.dialect;
16 import hunt.entity.EntityOption;
17 import hunt.entity.eql;
18 import hunt.logging;
19 import hunt.util.Common;
20 
21 import std.array;
22 import std.traits;
23 
24 /**
25  * 
26  */
27 class EntityManager : Closeable {
28 
29     Dialect _dialect;
30     EntityOption _option;
31     Database _db;
32     string _name;
33     private CriteriaBuilder _criteriaBuilder;
34     private EntityTransaction _transaction;
35     private EntitySession _entitySession;
36 
37     this(CriteriaBuilder criteriaBuilder, string name, EntityOption option,
38             Database db, Dialect dialect) {
39         assert(db !is null, "The database can't be null");
40         _criteriaBuilder = criteriaBuilder;
41         _name = name;
42         _option = option;
43         _db = db;
44         _dialect = dialect;
45         // _entitySession = new EntitySession(db);
46     }
47 
48     ~this() {
49         // version(HUNT_ENTITY_DEBUG) infof("Closing EntityManager in ~this()"); // bug
50         // close();
51     }
52 
53     T persist(T)(ref T entity) {
54         QueryBuilder builder = _db.createQueryBuilder(); // _factory.createQueryBuilder();
55         EntityInfo!T info = new EntityInfo!(T)(this, entity);
56         builder.insert(info.getTableName()).values(info.getInsertString());
57 
58         string autoIncrementKey = info.getAutoIncrementKey();
59         if (!autoIncrementKey.empty())
60             builder.setAutoIncrease(autoIncrementKey);
61         Statement stmt = getSession().prepare(builder.toString);
62         int r = stmt.execute(autoIncrementKey);
63 
64         version (HUNT_ENTITY_DEBUG) {
65             infof("affected: %d, autoIncrementKey: %s, lastInsertId: %d", r,
66                     autoIncrementKey, stmt.lastInsertId());
67         }
68 
69         info.setIncreaseKey(entity, stmt.lastInsertId);
70         return entity;
71     }
72 
73     T find(T, P)(P primaryKeyOrT) {
74         CriteriaBuilder criteriaBuilder = getCriteriaBuilder();
75         CriteriaQuery!T criteriaQuery = criteriaBuilder.createQuery!(T);
76         Root!T r;
77         Predicate condition;
78         static if (is(P == T)) {
79             r = criteriaQuery.from(primaryKeyOrT);
80             condition = criteriaBuilder.equal(r.getPrimaryField());
81         } else {
82             r = criteriaQuery.from();
83             condition = criteriaBuilder.equal(r.getPrimaryField(), primaryKeyOrT);
84         }
85         TypedQuery!T query = createQuery(criteriaQuery.select(r).where(condition));
86         return cast(T)(query.getSingleResult());
87     }
88 
89     int remove(T, P)(P primaryKeyOrT) {
90         CriteriaBuilder criteriaBuilder = getCriteriaBuilder();
91         CriteriaDelete!T criteriaDelete = criteriaBuilder.createCriteriaDelete!(T);
92         Root!T r;
93         Predicate condition;
94         static if (is(P == T)) {
95             r = criteriaDelete.from(primaryKeyOrT);
96             condition = criteriaBuilder.equal(r.getPrimaryField());
97         } else {
98             r = criteriaDelete.from();
99             condition = criteriaBuilder.equal(r.getPrimaryField(), primaryKeyOrT);
100         }
101         return createQuery(criteriaDelete.where(condition)).executeUpdate();
102     }
103 
104     int merge(T)(T entity) {
105         CriteriaBuilder criteriaBuilder = getCriteriaBuilder();
106         CriteriaUpdate!T criteriaUpdate = criteriaBuilder.createCriteriaUpdate!(T);
107         Root!T r = criteriaUpdate.from(entity);
108         Predicate condition = criteriaBuilder.equal(r.getPrimaryField());
109 
110         string primaryKey = r.getEntityInfo().getPrimaryKeyString();
111 
112         EntityFieldInfo[string] fields = r.getEntityInfo().getFields();
113 
114         foreach (string k, EntityFieldInfo v; fields) {
115             string columnName = v.getColumnName();
116 
117             version (HUNT_ENTITY_DEBUG)
118                 tracef("Field: %s, Column: %s", k, columnName);
119             if (columnName == primaryKey || columnName.empty()) {
120                 version (HUNT_ENTITY_DEBUG)
121                     warningf("primaryKey skipped, Field: %s, Column: %s", k, columnName);
122                 continue;
123             }
124 
125             criteriaUpdate.set(v);
126         }
127         return createQuery(criteriaUpdate.where(condition)).executeUpdate();
128     }
129 
130     void flush() {
131         //TODO 
132     }
133 
134     EqlQuery!(T) createQuery(T...)(string eql) {
135         return new EqlQuery!(T)(eql, this);
136     }
137 
138     EqlQuery!(T) createQuery(T...)(string query_eql, Pageable page) {
139         return new EqlQuery!(T)(query_eql, page, this);
140     }
141 
142     TypedQuery!(T, F) createQuery(T, F)(CriteriaQuery!(T, F) query) {
143         return new TypedQuery!(T, F)(query, this);
144     }
145 
146     Query!(T) createQuery(T)(CriteriaDelete!T query) {
147         return new Query!(T)(query, this);
148     }
149 
150     Query!(T) createQuery(T)(CriteriaUpdate!T query) {
151         return new Query!(T)(query, this);
152     }
153 
154     NativeQuery createNativeQuery(string sql) {
155         return new NativeQuery(this, sql);
156     }
157 
158     Dialect getDialect() {
159         return _dialect;
160     }
161 
162     EntitySession getSession() {
163         if (_entitySession is null) {
164             version (HUNT_DEBUG) {
165                 info("Creating a new session");
166             }
167             _entitySession = new EntitySession(_db);
168         }
169         return _entitySession;
170     }
171 
172     CriteriaBuilder getCriteriaBuilder() {
173         return _criteriaBuilder.setManager(this);
174     }
175 
176     EntityTransaction getTransaction() {
177         if(_transaction is null) {
178             _transaction = new EntityTransaction(getSession());
179         }
180         return _transaction;
181     }
182 
183     deprecated("Using getSession instead.") Database getDatabase() {
184         return _db;
185     }
186 
187     string getDbPoolInfo() {
188         return _db.poolInfo();
189     }
190 
191     DatabaseOption getDbOption() {
192         return _db.getOption();
193     }
194 
195     string getPrefix() {
196         return _option.database.prefix;
197     }
198 
199     void close() {
200         if (_entitySession) {
201             _entitySession.close();
202         }
203     }
204 }