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.Entity; 13 14 import hunt.entity; 15 import std.string; 16 import std.traits; 17 18 import hunt.logging; 19 20 // TODO: Tasks pending completion -@zhangxueping at 2021-02-19T15:26:35+08:00 21 // 22 __gshared EntityMetaInfo[string] avaliableEntities; 23 24 mixin template MakeModel() 25 { 26 import hunt.serialization.Common; 27 import hunt.validation; 28 import hunt.logging; 29 import std.format; 30 import hunt.entity.EntityMetaInfo; 31 32 void onInitialized() { 33 } 34 35 // pragma(msg, makeGetFunction!(typeof(this))); 36 37 // FIXME: Needing refactor or cleanup -@zhangxueping at 2020-08-28T16:59:53+08:00 38 // 39 // enum EntityMetaInfo metaInfo = extractEntityInfo!(typeof(this)); 40 41 static EntityMetaInfo metaInfo() { 42 enum m = extractEntityInfo!(typeof(this)); 43 if(!_isRegistered) { 44 _isRegistered = true; 45 synchronized { 46 avaliableEntities[m.fullName] = m; 47 } 48 } 49 return m; 50 } 51 52 private static bool _isRegistered = false; 53 54 mixin MakeLazyData; 55 56 mixin MakeLazyLoadList!(typeof(this)); 57 mixin MakeLazyLoadSingle!(typeof(this)); 58 mixin(makeGetFunction!(typeof(this))); 59 mixin MakeValid; 60 /* 61 * NOTE: annotation following code forbid auto create tables function when the EntityFactory construct for the reason when 62 * the Entity model import other Entity model cause the dlang bug like "object.Error@src/rt/minfo.d(371): Cyclic dependency between 63 * module SqlStruct.User and SqlStruct.Blog". 64 * if you want use the auto create tables function, you can open the following code and put all Entity model into one d file or 65 * use EntityManagerFactory.createTables!(User,Blog) after EntityManagerFactory construct. 66 */ 67 // shared static this() { 68 // addCreateTableHandle(getEntityTableName!(typeof(this)), &onCreateTableHandler!(typeof(this))); 69 // } 70 } 71 72 mixin template MakeLazyData() { 73 import hunt.logging; 74 75 @Ignore 76 private LazyData[string] _lazyDatas; 77 78 @Ignore 79 private EntityManager _manager; 80 81 void setManager(EntityManager manager) {_manager = manager;} 82 EntityManager getManager() {return _manager;} 83 84 void addLazyData(string key, LazyData data) { 85 if (data is null) { 86 version(HUNT_ENTITY_DEBUG) warningf("No data for %s", key); 87 } else { 88 _lazyDatas[key] = data; 89 } 90 } 91 92 LazyData[string] getAllLazyData() { 93 return _lazyDatas; 94 } 95 96 bool hasLazyData(string key) { 97 auto itemPtr = key in _lazyDatas; 98 return itemPtr !is null; 99 } 100 101 LazyData getLazyData(string key ) { 102 version(HUNT_ENTITY_DEBUG) { 103 tracef("key: %s, lazyDatas size: %d", key, _lazyDatas.length); 104 } 105 106 auto itemPtr = key in _lazyDatas; 107 108 if(itemPtr is null) { 109 warningf("No data found for [%s]", key); 110 return null; 111 } else { 112 return *itemPtr; 113 } 114 } 115 } 116 117 mixin template MakeLazyLoadList(T) { 118 119 private R[] lazyLoadList(R)(LazyData data , bool manyToMany = false , string mapped = "") { 120 import hunt.logging; 121 // logDebug("lazyLoadList ETMANAGER : ",_manager); 122 // assert(data !is null, "The LazyData can't be null"); 123 if(data is null) { 124 warningf("The LazyData for %s[] is null", R.stringof); 125 return null; 126 } 127 128 auto builder = _manager.getCriteriaBuilder(); 129 auto criteriaQuery = builder.createQuery!(R, T); 130 131 // logDebug("****LoadList( %s , %s , %s )".format(R.stringof,data.key,data.value)); 132 133 if(manyToMany) 134 { 135 // logDebug("lazyLoadList for :",mapped); 136 auto r = criteriaQuery.manyToManyFrom(null, this,mapped); 137 138 auto p = builder.lazyManyToManyEqual(r.get(data.key), data.value, false); 139 140 auto query = _manager.createQuery(criteriaQuery.select(r).where(p)); 141 auto ret = query.getResultList(); 142 foreach(v;ret) { 143 v.setManager(_manager); 144 } 145 return ret; 146 } 147 else 148 { 149 auto r = criteriaQuery.from(null, this); 150 auto p = builder.lazyEqual(r.get(data.key), data.value, false); 151 152 auto query = _manager.createQuery(criteriaQuery.select(r).where(p)); 153 auto ret = query.getResultList(); 154 foreach(v;ret) { 155 v.setManager(_manager); 156 } 157 return ret; 158 } 159 160 } 161 } 162 163 164 mixin template MakeLazyLoadSingle(T) { 165 166 import hunt.logging; 167 168 private R lazyLoadSingle(R)(LazyData data) { 169 if(data is null) { 170 warning("The LazyData for %s is null", R.stringof); 171 return R.init; 172 } 173 auto builder = _manager.getCriteriaBuilder(); 174 auto criteriaQuery = builder.createQuery!(R, T); 175 auto r = criteriaQuery.from(null, this); 176 auto p = builder.lazyEqual(r.get(data.key), data.value, false); 177 TypedQuery!(R, T) query = _manager.createQuery(criteriaQuery.select(r).where(p)); 178 Object singleResult = query.getSingleResult(); 179 180 if(singleResult is null) { 181 warningf("The result (%s) is null", R.stringof); 182 return R.init; 183 } 184 185 R ret = cast(R)(singleResult); 186 if(ret is null) { 187 warningf("Type missmatched, expect: %s, actural: %s", typeid(R), typeid(singleResult)); 188 } 189 ret.setManager(_manager); 190 return ret; 191 } 192 } 193 194 string makeGetFunction(T)() { 195 string str; 196 string allGetMethods; 197 198 static foreach (string memberName; FieldNameTuple!T) {{ 199 alias currentMemeber = __traits(getMember, T, memberName); 200 alias memType = typeof(currentMemeber); 201 202 static if (__traits(getProtection, currentMemeber) == "public") { 203 204 static if (hasUDA!(currentMemeber, OneToOne)) { 205 enum bool lazyLoading = true; 206 enum FetchType fetchType = getUDAs!(currentMemeber, OneToOne)[0].fetch; 207 } else static if (hasUDA!(currentMemeber, OneToMany)) { 208 enum bool lazyLoading = true; 209 enum FetchType fetchType = getUDAs!(currentMemeber, OneToMany)[0].fetch; 210 } else static if (hasUDA!(currentMemeber, ManyToOne)) { 211 enum bool lazyLoading = true; 212 enum FetchType fetchType = getUDAs!(currentMemeber, ManyToOne)[0].fetch; 213 } else static if (hasUDA!(currentMemeber, ManyToMany)) { 214 enum bool lazyLoading = true; 215 enum FetchType fetchType = getUDAs!(currentMemeber, ManyToMany)[0].fetch; 216 } else { 217 enum bool lazyLoading = false; 218 } 219 220 static if (lazyLoading) { 221 static if(fetchType == FetchType.EAGER && !hasUDA!(currentMemeber, JoinColumn)) { 222 allGetMethods ~= ` 223 if(` ~ memberName ~ ` is null) { 224 info("loading data for [` ~ memberName ~ `] in [` ~ T.stringof ~ `]"); 225 get` ~ capitalize(memberName) ~ `(); 226 } 227 `; 228 } 229 230 str ~= ` 231 public `~memType.stringof ~ " get" ~ capitalize(memberName) ~ `() {`; 232 233 static if (isArray!memType) { 234 string mappedBy; 235 static if(hasUDA!(currentMemeber, ManyToMany)) { 236 str ~= "\n bool manyToMany = true ;"; 237 238 mappedBy = "\"" ~ getUDAs!(currentMemeber, ManyToMany)[0].mappedBy ~ "\""; 239 } else { 240 str ~= "\n bool manyToMany = false ;"; 241 } 242 243 str ~= "\n" ~ memberName ~ ` = lazyLoadList!(` ~ memType.stringof.replace("[]","") ~ 244 `)(getLazyData("`~memberName~`"), manyToMany, `~mappedBy~`);`; 245 } else { 246 str ~= "\n" ~ memberName~` = lazyLoadSingle!(`~memType.stringof~`)(getLazyData("`~memberName~`"));`; 247 } 248 249 str ~= ` 250 return `~memberName~`; 251 }`; 252 } 253 } 254 }} 255 256 // Try to load the other members which is not loaed in current mapping. 257 258 str ~= "\n"; 259 str ~= ` 260 void loadLazyMembers() { 261 version(HUNT_ENTITY_DEBUG) { 262 infof("Try to load data for all the other object members in %s", typeid(` ~ T.stringof ~ `)); 263 } 264 ` ~ allGetMethods ~ ` 265 } 266 `; 267 268 return str; 269 } 270