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.EntityFieldOneToMany;
13 
14 import hunt.logging;
15 import hunt.entity;
16 import hunt.Exceptions;
17 import std.variant;
18 
19 
20 class EntityFieldOneToMany(T : Object, F : Object) : EntityFieldObject!(T,F) {
21 
22 
23     private OneToMany _mode;
24     private string _primaryKey;
25     // private string _joinColumn;
26     private string _findString;
27     private T[][string] _decodeCache;
28     
29     this(EntityManager manager, string fieldName, string primaryKey, string tableName, OneToMany mode, F owner) {
30         super(manager, fieldName, "", tableName, null, owner);
31         init(primaryKey, mode, owner);
32     }
33 
34     private void init(string primaryKey,  OneToMany mode, F owner) {
35         _mode = mode;       
36         _enableJoin = _mode.fetch == FetchType.EAGER;    
37         _primaryKey = primaryKey;
38         _joinColumn = _entityInfo.getFields[_mode.mappedBy].getJoinColumn();
39         initJoinData();
40         initFindStr();
41     }
42 
43     private void initJoinData() {
44         _joinSqlData = new JoinSqlBuild(); 
45         _joinSqlData.tableName = _entityInfo.getTableName();
46         _joinSqlData.joinWhere = getTableName() ~ "." ~ _primaryKey ~ " = " ~ _entityInfo.getTableName() ~ "." ~ _joinColumn;
47         _joinSqlData.joinType = JoinType.LEFT;
48         foreach(value; _entityInfo.getFields()) {
49             _joinSqlData.columnNames ~= value.getSelectColumn();
50         }
51         // logDebug("one to many join sql : %s ".format(_joinSqlData));
52     }
53 
54     override string getSelectColumn() {
55         return "";
56     }
57 
58 
59     private void initFindStr() {
60         _findString = "SELECT ";
61         string[] el;
62         foreach(k,v; _entityInfo.getFields()) {
63             if (v.getSelectColumn() != "")
64                 el ~= " "~v.getSelectColumn();
65         }
66         foreach(k,v; el) {
67             _findString ~= v;
68             if (k != el.length -1)
69                 _findString ~= ",";
70         }
71         _findString ~= " FROM "~_entityInfo.getTableName()~" WHERE " ~ _entityInfo.getTableName() ~"."~_joinColumn~" = ";
72     }
73 
74     string getPrimaryKey() {return _primaryKey;}
75     OneToMany getMode() {return _mode;}
76 
77     override FetchType fetchType() {
78         return _mode.fetch;
79     }    
80 
81 
82     T[] deSerialize(Row[] rows, int startIndex, bool isFromManyToOne, F owner) {
83         version(HUNT_ENTITY_DEBUG) {
84             tracef("FetchType: %s, isFromManyToOne: %s", _mode.fetch, isFromManyToOne);
85         }
86 
87         if (_mode.fetch == FetchType.LAZY)
88             return null;
89 
90         T[] ret;
91         T singleRet;
92         long count = -1;
93         if (isFromManyToOne) {
94             string joinValue = getJoinKeyValue(rows[startIndex]);
95             if (joinValue == "")
96                 return ret;
97             T[] rets = getValueByJoinKeyValue(joinValue);
98             foreach(value; rets) {
99                 ret ~= Common.sampleCopy(value);
100             }
101         } else {
102             version(HUNT_ENTITY_DEBUG) warning("start");
103             foreach(value; rows) {
104                 singleRet = _entityInfo.deSerialize([value], count, 0, owner);
105                 if (singleRet !is null)
106                     // ret ~= Common.sampleCopy(singleRet);
107                     ret ~= singleRet; // Common.sampleCopy(singleRet);
108             }
109             version(HUNT_ENTITY_DEBUG) warning("ended");
110         }
111         return ret;
112     }
113 
114     private string getJoinKeyValue(Row row) {
115         string name = EntityExpression.getColumnAsName(_primaryKey, getTableName());
116         Variant v = row.getValue(name);
117         if(!v.hasValue()) {
118             version(HUNT_DEBUG) warningf("Can't find value for %s", name);
119             return null;
120         }
121         
122         string value = v.toString();
123         version(HUNT_ENTITY_DEBUG) tracef("A column: %s = %s", name, value);
124         return value;        
125     }
126 
127     private T[] getValueByJoinKeyValue(string key) {
128         if (key in _decodeCache)
129             return _decodeCache[key];
130 
131         T[] ret;
132         T singleRet;
133         auto stmt = _manager.getSession().prepare(_findString~key);
134 		auto res = stmt.query();
135         long count = -1;
136         foreach(value; res) {
137             singleRet = _entityInfo.deSerialize([value], count);
138             if (singleRet)
139                 ret ~=  singleRet;
140         }
141         _decodeCache[key] = ret;
142         return _decodeCache[key];
143     }
144 
145     void setMode(OneToMany mode) {
146         _mode = mode;
147         _enableJoin = _mode.fetch == FetchType.EAGER;    
148     }
149 
150     override LazyData getLazyData(Row row) {
151         string name = EntityExpression.getColumnAsName(_primaryKey, getTableName());
152         Variant v = row.getValue(name);
153         if(!v.hasValue()) {
154             version(HUNT_ENTITY_DEBUG) warningf("Can't find the value for the primary key: %s", name);
155             return null;
156         }
157         
158         string value = v.toString();
159         version(HUNT_ENTITY_DEBUG) {
160             infof("The referenced primary column: %s=%s, the current model: %s", 
161                 name, value, T.stringof);
162         }
163 
164         LazyData ret = new LazyData(_mode.mappedBy, value);
165         return ret;        
166     }
167 
168 
169 
170 }