From 88917aa2ef3370251d6fec0ff3de5e074a13394a Mon Sep 17 00:00:00 2001
From: Jinzhu <wosmvp@gmail.com>
Date: Sun, 17 Nov 2013 08:55:37 +0800
Subject: [PATCH] Cache model struct for less GC

---
 model.go | 75 +++++++++++++++++++++++++++++++++++---------------------
 1 file changed, 47 insertions(+), 28 deletions(-)

diff --git a/model.go b/model.go
index a9f7440e..a008330b 100644
--- a/model.go
+++ b/model.go
@@ -8,6 +8,12 @@ import (
 	"time"
 )
 
+var modelFieldMap map[string][]reflect.StructField
+
+func init() {
+	modelFieldMap = map[string][]reflect.StructField{}
+}
+
 type Model struct {
 	data          interface{}
 	do            *Do
@@ -39,45 +45,58 @@ func (m *Model) primaryKeyDb() string {
 	return toSnake(m.primaryKey())
 }
 
+func getStructs(typ reflect.Type) (fs []reflect.StructField) {
+	name := typ.Name()
+	if fs = modelFieldMap[name]; fs != nil {
+		return
+	}
+
+	for i := 0; i < typ.NumField(); i++ {
+		p := typ.Field(i)
+		if !p.Anonymous && ast.IsExported(p.Name) {
+			fs = append(fs, p)
+		}
+	}
+
+	modelFieldMap[name] = fs
+	return
+}
+
 func (m *Model) fields(operation string) (fields []*Field) {
 	indirect_value := m.reflectData()
 	if !indirect_value.IsValid() {
 		return
 	}
 
-	typ := indirect_value.Type()
-	for i := 0; i < typ.NumField(); i++ {
-		p := typ.Field(i)
-		if !p.Anonymous && ast.IsExported(p.Name) {
-			var field Field
-			field.Name = p.Name
-			field.dbName = toSnake(p.Name)
-			field.isPrimaryKey = m.primaryKeyDb() == field.dbName
-			value := indirect_value.FieldByName(p.Name)
-			field.model = m
+	for _, filed_struct := range getStructs(indirect_value.Type()) {
+		var field Field
+		field.Name = filed_struct.Name
+		field.dbName = toSnake(filed_struct.Name)
+		field.isPrimaryKey = m.primaryKeyDb() == field.dbName
+		value := indirect_value.FieldByName(filed_struct.Name)
+		field.model = m
 
-			if time_value, is_time := value.Interface().(time.Time); is_time {
-				field.autoCreateTime = "created_at" == field.dbName
-				field.autoUpdateTime = "updated_at" == field.dbName
+		if time_value, is_time := value.Interface().(time.Time); is_time {
+			field.autoCreateTime = "created_at" == field.dbName
+			field.autoUpdateTime = "updated_at" == field.dbName
 
-				switch operation {
-				case "create":
-					if (field.autoCreateTime || field.autoUpdateTime) && time_value.IsZero() {
-						value.Set(reflect.ValueOf(time.Now()))
-					}
-				case "update":
-					if field.autoUpdateTime {
-						value.Set(reflect.ValueOf(time.Now()))
-					}
+			switch operation {
+			case "create":
+				if (field.autoCreateTime || field.autoUpdateTime) && time_value.IsZero() {
+					value.Set(reflect.ValueOf(time.Now()))
+				}
+			case "update":
+				if field.autoUpdateTime {
+					value.Set(reflect.ValueOf(time.Now()))
 				}
 			}
-
-			field.structField = p
-			field.reflectValue = value
-			field.Value = value.Interface()
-			field.parseAssociation()
-			fields = append(fields, &field)
 		}
+
+		field.structField = filed_struct
+		field.reflectValue = value
+		field.Value = value.Interface()
+		field.parseAssociation()
+		fields = append(fields, &field)
 	}
 	return
 }