Finish most codes of analysis.
diff --git a/oal-parser/src/main/java/org/apache/skywalking/oal/tool/output/FileGenerator.java b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/output/FileGenerator.java
index dd02273..a8dbc1e 100644
--- a/oal-parser/src/main/java/org/apache/skywalking/oal/tool/output/FileGenerator.java
+++ b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/output/FileGenerator.java
@@ -55,4 +55,12 @@
     void generateRemoteWorker(AnalysisResult result, Writer output) throws IOException, TemplateException {
         configuration.getTemplate("RemoteWorkerTemplate.ftl").process(result, output);
     }
+
+    void generatePersistentWorker(AnalysisResult result, Writer output) throws IOException, TemplateException {
+        configuration.getTemplate("PersistentWorkerTemplate.ftl").process(result, output);
+    }
+
+    void generateIndicatorImplementor(AnalysisResult result, Writer output) throws IOException, TemplateException {
+        configuration.getTemplate("IndicatorImplementor.ftl").process(result, output);
+    }
 }
diff --git a/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/AnalysisResult.java b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/AnalysisResult.java
index 8c599e1..69ac3c4 100644
--- a/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/AnalysisResult.java
+++ b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/AnalysisResult.java
@@ -18,6 +18,8 @@
 
 package org.apache.skywalking.oal.tool.parser;
 
+import java.util.LinkedList;
+import java.util.List;
 import lombok.AccessLevel;
 import lombok.Getter;
 import lombok.Setter;
@@ -36,5 +38,69 @@
 
     private String aggregationFunctionName;
 
+    private String indicatorClassName;
+
     private Selector remoteSelector;
+
+    private boolean needMerge;
+
+    private EntryMethod entryMethod;
+
+    private List<DataColumn> persistentFields;
+
+    private List<SourceColumn> fieldsFromSource;
+
+    private PersistenceColumns serializeFields;
+
+    public void addPersistentField(String fieldName, String columnName, Class<?> type) {
+        if (persistentFields == null) {
+            persistentFields = new LinkedList<>();
+        }
+        DataColumn dataColumn = new DataColumn(fieldName, columnName, type);
+        persistentFields.add(dataColumn);
+    }
+
+    public void generateSerializeFields() {
+        serializeFields = new PersistenceColumns();
+        serializeFields.addLongField("timeBucket");
+        for (SourceColumn sourceColumn : fieldsFromSource) {
+            String type = sourceColumn.getType().getSimpleName();
+            switch (type) {
+                case "int":
+                    serializeFields.addIntField(sourceColumn.getFieldName());
+                    break;
+                case "double":
+                    serializeFields.addDoubleField(sourceColumn.getFieldName());
+                    break;
+                case "String":
+                    serializeFields.addStringField(sourceColumn.getFieldName());
+                    break;
+                case "long":
+                    serializeFields.addLongField(sourceColumn.getFieldName());
+                    break;
+                default:
+                    throw new IllegalStateException("Unexpected field type [" + type + "] of source sourceColumn [" + sourceColumn.getFieldName() + "]");
+            }
+        }
+
+        for (DataColumn column : persistentFields) {
+            String type = column.getType().getSimpleName();
+            switch (type) {
+                case "int":
+                    serializeFields.addIntField(column.getFieldName());
+                    break;
+                case "double":
+                    serializeFields.addDoubleField(column.getFieldName());
+                    break;
+                case "String":
+                    serializeFields.addStringField(column.getFieldName());
+                    break;
+                case "long":
+                    serializeFields.addLongField(column.getFieldName());
+                    break;
+                default:
+                    throw new IllegalStateException("Unexpected field type [" + type + "] of persistence column [" + column.getFieldName() + "]");
+            }
+        }
+    }
 }
diff --git a/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/DataColumn.java b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/DataColumn.java
new file mode 100644
index 0000000..5cb2363
--- /dev/null
+++ b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/DataColumn.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oal.tool.parser;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.skywalking.oal.tool.util.ClassMethodUtil;
+
+@Getter(AccessLevel.PUBLIC)
+@Setter(AccessLevel.PUBLIC)
+public class DataColumn {
+    private String fieldName;
+    private String columnName;
+    private Class<?> type;
+    private String typeName;
+    private String fieldSetter;
+    private String fieldGetter;
+
+    public DataColumn(String fieldName, String columnName, Class<?> type) {
+        this.fieldName = fieldName;
+        this.columnName = columnName;
+        this.type = type;
+        this.typeName = type.getName();
+
+        this.fieldGetter = ClassMethodUtil.toGetMethod(fieldName);
+        this.fieldSetter = ClassMethodUtil.toSetMethod(fieldName);
+    }
+}
diff --git a/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/DeepAnalysis.java b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/DeepAnalysis.java
new file mode 100644
index 0000000..c47ffd2
--- /dev/null
+++ b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/DeepAnalysis.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oal.tool.parser;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.List;
+import org.apache.skywalking.oal.tool.util.ClassMethodUtil;
+import org.apache.skywalking.oap.server.core.Indicators;
+import org.apache.skywalking.oap.server.core.analysis.indicator.Indicator;
+import org.apache.skywalking.oap.server.core.analysis.indicator.annotation.ConstOne;
+import org.apache.skywalking.oap.server.core.analysis.indicator.annotation.Entrance;
+import org.apache.skywalking.oap.server.core.analysis.indicator.annotation.IndicatorType;
+import org.apache.skywalking.oap.server.core.analysis.indicator.annotation.SourceFrom;
+import org.apache.skywalking.oap.server.core.storage.annotation.Column;
+
+public class DeepAnalysis {
+    public AnalysisResult analysis(AnalysisResult result) {
+        // 1. Set sub package name by source.metric
+        result.setPackageName(result.getSourceName().toLowerCase() + "." + result.getMetricName().toLowerCase());
+
+        Class<? extends Indicator> indicatorClass = Indicators.find(result.getAggregationFunctionName());
+
+        // 2. based on class annotation, find selector and merge requirement
+        IndicatorType indicatorClassAnnotation = indicatorClass.getAnnotation(IndicatorType.class);
+        if (indicatorClassAnnotation == null) {
+            throw new IllegalArgumentException("Can't find IndicatorType in class: " + indicatorClass.getName());
+        }
+        result.setIndicatorClassName(indicatorClass.getSimpleName());
+        result.setRemoteSelector(indicatorClassAnnotation.selector());
+        result.setNeedMerge(indicatorClassAnnotation.needMerge());
+
+        // 3. Find Entrance method of this indicator
+        Class c = indicatorClass;
+        Method entranceMethod = null;
+        SearchEntrance:
+        while (!c.equals(Object.class)) {
+            for (Method method : c.getMethods()) {
+                Entrance annotation = method.getAnnotation(Entrance.class);
+                if (annotation != null) {
+                    entranceMethod = method;
+                    break SearchEntrance;
+                }
+            }
+            c = c.getSuperclass();
+        }
+        if (entranceMethod == null) {
+            throw new IllegalArgumentException("Can't find Entrance method in class: " + indicatorClass.getName());
+        }
+        EntryMethod entryMethod = new EntryMethod();
+        result.setEntryMethod(entryMethod);
+        entryMethod.setMethodName(entranceMethod.getName());
+
+        // 4. Use parameter's annotation of entrance method to generate aggregation entrance.
+        for (Parameter parameter : entranceMethod.getParameters()) {
+            Annotation[] parameterAnnotations = parameter.getAnnotations();
+            if (parameterAnnotations == null || parameterAnnotations.length == 0) {
+                throw new IllegalArgumentException("Entrance method:" + entranceMethod + " doesn't include the annotation.");
+            }
+            Annotation annotation = parameterAnnotations[0];
+            if (annotation instanceof SourceFrom) {
+                entryMethod.addArg("source." + ClassMethodUtil.toGetMethod(result.getSourceAttribute()) + "()");
+            } else if (annotation instanceof ConstOne) {
+                entryMethod.addArg("1");
+            } else {
+                throw new IllegalArgumentException("Entrance method:" + entranceMethod + " doesn't the expected annotation.");
+            }
+        }
+
+        // 5. Get all column declared in Indicator class.
+        c = indicatorClass;
+        while (!c.equals(Object.class)) {
+            for (Field field : c.getDeclaredFields()) {
+                Column column = field.getAnnotation(Column.class);
+                if (column != null) {
+                    result.addPersistentField(field.getName(), column.columnName(), field.getType());
+                }
+            }
+            c = c.getSuperclass();
+        }
+
+        // 6. Based on Source, generate default columns
+        List<SourceColumn> columns = SourceColumnsFactory.getColumns(result.getSourceName());
+        result.setFieldsFromSource(columns);
+
+        result.generateSerializeFields();
+
+        return result;
+    }
+
+}
diff --git a/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/EntryMethod.java b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/EntryMethod.java
new file mode 100644
index 0000000..dc2f6cd
--- /dev/null
+++ b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/EntryMethod.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oal.tool.parser;
+
+import java.util.LinkedList;
+import java.util.List;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter(AccessLevel.PUBLIC)
+@Setter(AccessLevel.PUBLIC)
+public class EntryMethod {
+    private String methodName;
+
+    private List<String> argsExpressions;
+
+    public void addArg(String expression) {
+        if (argsExpressions == null) {
+            argsExpressions = new LinkedList<>();
+        }
+        argsExpressions.add(expression);
+    }
+}
diff --git a/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/OALListener.java b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/OALListener.java
index ca0d715..3d73b3f 100644
--- a/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/OALListener.java
+++ b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/OALListener.java
@@ -38,8 +38,8 @@
 
     @Override
     public void exitAggregationStatement(@NotNull OALParser.AggregationStatementContext ctx) {
-        current.setPackageName(current.getSourceName().toLowerCase() + "." + current.getMetricName().toLowerCase());
-        results.add(current);
+        DeepAnalysis deepAnalysis = new DeepAnalysis();
+        results.add(deepAnalysis.analysis(current));
         current = null;
     }
 
diff --git a/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/PersistenceColumns.java b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/PersistenceColumns.java
new file mode 100644
index 0000000..448755d
--- /dev/null
+++ b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/PersistenceColumns.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oal.tool.parser;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class PersistenceColumns {
+    private List<PersistenceField> stringFields = new LinkedList<>();
+    private List<PersistenceField> longFields = new LinkedList<>();
+    private List<PersistenceField> doubleFields = new LinkedList<>();
+    private List<PersistenceField> intFields = new LinkedList<>();
+
+    public void addStringField(String fieldName) {
+        stringFields.add(new PersistenceField(fieldName));
+    }
+
+    public void addLongField(String fieldName) {
+        longFields.add(new PersistenceField(fieldName));
+    }
+
+    public void addDoubleField(String fieldName) {
+        doubleFields.add(new PersistenceField(fieldName));
+    }
+
+    public void addIntField(String fieldName) {
+        intFields.add(new PersistenceField(fieldName));
+    }
+
+    public List<PersistenceField> getStringFields() {
+        return stringFields;
+    }
+
+    public List<PersistenceField> getLongFields() {
+        return longFields;
+    }
+
+    public List<PersistenceField> getDoubleFields() {
+        return doubleFields;
+    }
+
+    public List<PersistenceField> getIntFields() {
+        return intFields;
+    }
+}
diff --git a/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/PersistenceField.java b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/PersistenceField.java
new file mode 100644
index 0000000..c82aca8
--- /dev/null
+++ b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/PersistenceField.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oal.tool.parser;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.skywalking.oal.tool.util.ClassMethodUtil;
+
+@Getter(AccessLevel.PUBLIC)
+@Setter(AccessLevel.PUBLIC)
+public class PersistenceField {
+    private String fieldName;
+    private String setter;
+    private String getter;
+
+    public PersistenceField(String fieldName) {
+        this.fieldName = fieldName;
+        this.setter = ClassMethodUtil.toSetMethod(fieldName);
+        this.getter = ClassMethodUtil.toGetMethod(fieldName);
+    }
+}
diff --git a/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/SourceColumn.java b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/SourceColumn.java
new file mode 100644
index 0000000..26b28c7
--- /dev/null
+++ b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/SourceColumn.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oal.tool.parser;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.skywalking.oal.tool.util.ClassMethodUtil;
+
+@Getter(AccessLevel.PUBLIC)
+@Setter(AccessLevel.PUBLIC)
+public class SourceColumn {
+    private String fieldName;
+    private String columnName;
+    private Class<?> type;
+    private String typeName;
+    private boolean isID;
+    private String fieldSetter;
+    private String fieldGetter;
+
+    public SourceColumn(String fieldName, String columnName, Class<?> type, boolean isID) {
+        this.fieldName = fieldName;
+        this.columnName = columnName;
+        this.type = type;
+        this.typeName = type.getSimpleName();
+        this.isID = isID;
+
+        this.fieldGetter = ClassMethodUtil.toGetMethod(fieldName);
+        this.fieldSetter = ClassMethodUtil.toSetMethod(fieldName);
+    }
+}
diff --git a/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/SourceColumnsFactory.java b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/SourceColumnsFactory.java
new file mode 100644
index 0000000..8b5524e
--- /dev/null
+++ b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/parser/SourceColumnsFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oal.tool.parser;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class SourceColumnsFactory {
+    public static List<SourceColumn> getColumns(String source) {
+        List<SourceColumn> columnList;
+        SourceColumn idColumn;
+        switch (source) {
+            case "Service":
+                columnList = new LinkedList<>();
+                idColumn = new SourceColumn("id", "id", int.class, true);
+                columnList.add(idColumn);
+                SourceColumn serviceIdColumn = new SourceColumn("serviceId", "serviceId", int.class, false);
+                columnList.add(serviceIdColumn);
+                SourceColumn serviceInstanceIdColumn = new SourceColumn("serviceInstanceId", "serviceInstanceId", int.class, false);
+                columnList.add(serviceInstanceIdColumn);
+                return columnList;
+            case "Endpoint":
+                columnList = new LinkedList<>();
+                idColumn = new SourceColumn("id", "id", int.class, true);
+                columnList.add(idColumn);
+                return columnList;
+            default:
+                throw new IllegalArgumentException("Illegal source :" + source);
+        }
+    }
+}
diff --git a/oal-parser/src/main/java/org/apache/skywalking/oal/tool/util/ClassMethodUtil.java b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/util/ClassMethodUtil.java
new file mode 100644
index 0000000..081bca5
--- /dev/null
+++ b/oal-parser/src/main/java/org/apache/skywalking/oal/tool/util/ClassMethodUtil.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oal.tool.util;
+
+public class ClassMethodUtil {
+    public static String toGetMethod(String attribute) {
+        return "get" + attribute.substring(0, 1).toUpperCase() + attribute.substring(1);
+    }
+
+    public static String toSetMethod(String attribute) {
+        return "set" + attribute.substring(0, 1).toUpperCase() + attribute.substring(1);
+    }
+}
diff --git a/oal-parser/src/main/resources/code-templates/AggregateWorkerTemplate.ftl b/oal-parser/src/main/resources/code-templates/AggregateWorkerTemplate.ftl
index 253f0fe..b46405e 100644
--- a/oal-parser/src/main/resources/code-templates/AggregateWorkerTemplate.ftl
+++ b/oal-parser/src/main/resources/code-templates/AggregateWorkerTemplate.ftl
@@ -16,7 +16,7 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core.analysis.${packageName};
+package org.apache.skywalking.oap.server.core.analysis.generated.${packageName};
 
 import org.apache.skywalking.oap.server.core.analysis.worker.AbstractAggregatorWorker;
 import org.apache.skywalking.oap.server.library.module.ModuleManager;
diff --git a/oal-parser/src/main/resources/code-templates/IndicatorImplementor.ftl b/oal-parser/src/main/resources/code-templates/IndicatorImplementor.ftl
new file mode 100644
index 0000000..714efb8
--- /dev/null
+++ b/oal-parser/src/main/resources/code-templates/IndicatorImplementor.ftl
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.core.analysis.generated.${packageName};
+
+import java.util.*;
+import lombok.*;
+import org.apache.skywalking.oap.server.core.analysis.indicator.*;
+import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData;
+import org.apache.skywalking.oap.server.core.storage.annotation.Column;
+
+/**
+ * This class is auto generated. Please don't change this class manually.
+ *
+ * @author Observability Analysis Language code generator
+ */
+
+public class ${metricName}Indicator extends ${indicatorClassName} {
+
+<#list fieldsFromSource as sourceField>
+    @Setter @Getter @Column(columnName = "${sourceField.columnName}") private ${sourceField.typeName} ${sourceField.fieldName};
+</#list>
+
+    @Override public String name() {
+        return ${metricName};
+    }
+
+    @Override public String id() {
+        return String.valueOf(id);
+    }
+
+    @Override public int hashCode() {
+        int result = 17;
+<#list fieldsFromSource as sourceField>
+    <#if sourceField.isID()>
+        result = 31 * result + ${sourceField.fieldName};
+    </#if>
+</#list>
+        result = 31 * result + (int)getTimeBucket();
+        return result;
+    }
+
+
+    @Override public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+
+        ${metricName}Indicator indicator = (${metricName}Indicator)obj;
+<#list fieldsFromSource as sourceField>
+    <#if sourceField.isID()>
+        if (${sourceField.fieldName} != indicator.${sourceField.fieldName})
+    </#if>
+</#list>
+            return false;
+        if (getTimeBucket() != indicator.getTimeBucket())
+            return false;
+
+        return true;
+    }
+
+
+    @Override public RemoteData.Builder serialize() {
+        RemoteData.Builder remoteBuilder = RemoteData.newBuilder();
+<#list serializeFields.stringFields as field>
+        remoteBuilder.setDataStrings(${field?index}, ${field.getter}());
+</#list>
+
+<#list serializeFields.longFields as field>
+        remoteBuilder.setDataLongs(${field?index}, ${field.getter}());
+</#list>
+
+<#list serializeFields.doubleFields as field>
+        remoteBuilder.setDataDoubles(${field?index}, ${field.getter}());
+</#list>
+
+<#list serializeFields.intFields as field>
+        remoteBuilder.setDataIntegers(${field?index}, ${field.getter}());
+</#list>
+
+        return remoteBuilder;
+    }
+
+    @Override public void deserialize(RemoteData remoteData) {
+<#list serializeFields.stringFields as field>
+        ${field.setter}(remoteData.getDataStrings(${field?index}));
+</#list>
+
+<#list serializeFields.longFields as field>
+        ${field.setter}(remoteData.getDataLongs(${field?index}));
+</#list>
+
+<#list serializeFields.doubleFields as field>
+        ${field.setter}(remoteData.getDataDoubles(${field?index}));
+</#list>
+
+<#list serializeFields.intFields as field>
+        ${field.setter}(remoteData.getDataIntegers(${field?index}));
+</#list>
+    }
+
+    @Override public Map<String, Object> toMap() {
+        Map<String, Object> map = new HashMap<>();
+<#list fieldsFromSource as field>
+        map.put("${field.columnName}", ${field.fieldGetter}());
+</#list>
+<#list persistentFields as field>
+        map.put("${field.columnName}", ${field.fieldGetter}());
+</#list>
+        return map;
+    }
+
+    @Override public Indicator newOne(Map<String, Object> dbMap) {
+        ${metricName}Indicator indicator = new ${metricName}Indicator();
+<#list fieldsFromSource as field>
+        indicator.${field.fieldSetter}((${field.typeName})dbMap.get("${field.columnName}"));
+</#list>
+<#list persistentFields as field>
+        indicator.${field.fieldSetter}((${field.typeName})dbMap.get("${field.columnName}"));
+</#list>
+        return indicator;
+    }
+}
diff --git a/oal-parser/src/main/resources/code-templates/PersistentWorkerTemplate.ftl b/oal-parser/src/main/resources/code-templates/PersistentWorkerTemplate.ftl
new file mode 100644
index 0000000..4c7848d
--- /dev/null
+++ b/oal-parser/src/main/resources/code-templates/PersistentWorkerTemplate.ftl
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.core.analysis.generated.${packageName};
+
+import org.apache.skywalking.oap.server.core.analysis.worker.AbstractPersistentWorker;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+
+/**
+ * This class is auto generated. Please don't change this class manually.
+ *
+ * @author Observability Analysis Language code generator
+ */
+public class ${metricName}PersistentWorker extends AbstractPersistentWorker<${metricName}Indicator> {
+
+    public ${metricName}PersistentWorker(ModuleManager moduleManager) {
+        super(moduleManager);
+    }
+
+    @Override protected boolean needMergeDBData() {
+        return ${needMerge?then('true', 'false')};
+    }
+}
diff --git a/oal-parser/src/main/resources/code-templates/RemoteWorkerTemplate.ftl b/oal-parser/src/main/resources/code-templates/RemoteWorkerTemplate.ftl
index 95872da..2156548 100644
--- a/oal-parser/src/main/resources/code-templates/RemoteWorkerTemplate.ftl
+++ b/oal-parser/src/main/resources/code-templates/RemoteWorkerTemplate.ftl
@@ -16,7 +16,7 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core.analysis.${packageName};
+package org.apache.skywalking.oap.server.core.analysis.generated.${packageName};
 
 import org.apache.skywalking.oap.server.core.analysis.worker.AbstractRemoteWorker;
 import org.apache.skywalking.oap.server.core.remote.selector.Selector;
diff --git a/oal-parser/src/test/java/org/apache/skywalking/oal/tool/output/FileGeneratorTest.java b/oal-parser/src/test/java/org/apache/skywalking/oal/tool/output/FileGeneratorTest.java
index b6b5b05..3761f7a 100644
--- a/oal-parser/src/test/java/org/apache/skywalking/oal/tool/output/FileGeneratorTest.java
+++ b/oal-parser/src/test/java/org/apache/skywalking/oal/tool/output/FileGeneratorTest.java
@@ -26,6 +26,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import org.apache.skywalking.oal.tool.parser.AnalysisResult;
+import org.apache.skywalking.oal.tool.parser.SourceColumnsFactory;
 import org.apache.skywalking.oap.server.core.remote.selector.Selector;
 import org.junit.Assert;
 import org.junit.Test;
@@ -39,6 +40,13 @@
         result.setMetricName("ServiceAvg");
         result.setAggregationFunctionName("avg");
         result.setRemoteSelector(Selector.HashCode);
+        result.setNeedMerge(true);
+        result.setIndicatorClassName("AvgIndicator");
+        result.addPersistentField("summation", "summation", long.class);
+        result.addPersistentField("count", "count", int.class);
+        result.addPersistentField("value", "value", long.class);
+        result.setFieldsFromSource(SourceColumnsFactory.getColumns("Service"));
+        result.generateSerializeFields();
 
         return result;
     }
@@ -73,6 +81,37 @@
         //fileGenerator.generateRemoteWorker(result, new OutputStreamWriter(System.out));
     }
 
+    @Test
+    public void testGeneratePersistentWorker() throws IOException, TemplateException {
+        AnalysisResult result = buildResult();
+
+        List<AnalysisResult> results = new LinkedList<>();
+        results.add(result);
+
+        FileGenerator fileGenerator = new FileGenerator(results, ".");
+        StringWriter writer = new StringWriter();
+        fileGenerator.generatePersistentWorker(result, writer);
+
+        Assert.assertEquals(readExpectedFile("PersistentWorkerExpected.java"), writer.toString());
+
+        //fileGenerator.generatePersistentWorker(result, new OutputStreamWriter(System.out));
+    }
+
+    @Test
+    public void testGenerateIndicatorImplementor() throws IOException, TemplateException {
+        AnalysisResult result = buildResult();
+
+        List<AnalysisResult> results = new LinkedList<>();
+        results.add(result);
+
+        FileGenerator fileGenerator = new FileGenerator(results, ".");
+        StringWriter writer = new StringWriter();
+        fileGenerator.generateIndicatorImplementor(result, writer);
+        Assert.assertEquals(readExpectedFile("IndicatorImplementorExpected.java"), writer.toString());
+
+        //fileGenerator.generateIndicatorImplementor(result, new OutputStreamWriter(System.out));
+    }
+
     private String readExpectedFile(String filename) throws IOException {
         BufferedReader reader = new BufferedReader(new InputStreamReader(FileGenerator.class.getResourceAsStream("/expectedFiles/" + filename)));
 
diff --git a/oal-parser/src/test/java/org/apache/skywalking/oal/tool/parser/DeepAnalysisTest.java b/oal-parser/src/test/java/org/apache/skywalking/oal/tool/parser/DeepAnalysisTest.java
new file mode 100644
index 0000000..1ef392f
--- /dev/null
+++ b/oal-parser/src/test/java/org/apache/skywalking/oal/tool/parser/DeepAnalysisTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oal.tool.parser;
+
+import java.util.List;
+import org.apache.skywalking.oap.server.core.remote.selector.Selector;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class DeepAnalysisTest {
+    @Test
+    public void testAnalysis(){
+        AnalysisResult result = new AnalysisResult();
+        result.setSourceName("Service");
+        result.setPackageName("service.serviceavg");
+        result.setSourceAttribute("latency");
+        result.setMetricName("ServiceAvg");
+        result.setAggregationFunctionName("avg");
+
+        DeepAnalysis analysis = new DeepAnalysis();
+        result = analysis.analysis(result);
+
+        Assert.assertEquals(Selector.HashCode, result.getRemoteSelector());
+        Assert.assertEquals(true, result.isNeedMerge());
+        EntryMethod method = result.getEntryMethod();
+        Assert.assertEquals("combine", method.getMethodName());
+        Assert.assertEquals("source.getLatency()", method.getArgsExpressions().get(0));
+        Assert.assertEquals("1", method.getArgsExpressions().get(1));
+
+        List<SourceColumn> source = result.getFieldsFromSource();
+        Assert.assertEquals(3, source.size());
+
+        List<DataColumn> persistentFields = result.getPersistentFields();
+        Assert.assertEquals(4, persistentFields.size());
+    }
+}
diff --git a/oal-parser/src/test/resources/expectedFiles/AggregateWorkerExpected.java b/oal-parser/src/test/resources/expectedFiles/AggregateWorkerExpected.java
index e79290c..320ce9c 100644
--- a/oal-parser/src/test/resources/expectedFiles/AggregateWorkerExpected.java
+++ b/oal-parser/src/test/resources/expectedFiles/AggregateWorkerExpected.java
@@ -16,7 +16,7 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core.analysis.service.serviceavg;
+package org.apache.skywalking.oap.server.core.analysis.generated.service.serviceavg;
 
 import org.apache.skywalking.oap.server.core.analysis.worker.AbstractAggregatorWorker;
 import org.apache.skywalking.oap.server.library.module.ModuleManager;
diff --git a/oal-parser/src/test/resources/expectedFiles/IndicatorImplementorExpected.java b/oal-parser/src/test/resources/expectedFiles/IndicatorImplementorExpected.java
new file mode 100644
index 0000000..6e80661
--- /dev/null
+++ b/oal-parser/src/test/resources/expectedFiles/IndicatorImplementorExpected.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.core.analysis.generated.service.serviceavg;
+
+import java.util.*;
+import lombok.*;
+import org.apache.skywalking.oap.server.core.analysis.indicator.*;
+import org.apache.skywalking.oap.server.core.remote.grpc.proto.RemoteData;
+import org.apache.skywalking.oap.server.core.storage.annotation.Column;
+
+/**
+ * This class is auto generated. Please don't change this class manually.
+ *
+ * @author Observability Analysis Language code generator
+ */
+
+public class ServiceAvgIndicator extends AvgIndicator {
+
+    @Setter @Getter @Column(columnName = "id") private int id;
+    @Setter @Getter @Column(columnName = "serviceId") private int serviceId;
+    @Setter @Getter @Column(columnName = "serviceInstanceId") private int serviceInstanceId;
+
+    @Override public String name() {
+        return ServiceAvg;
+    }
+
+    @Override public String id() {
+        return String.valueOf(id);
+    }
+
+    @Override public int hashCode() {
+        int result = 17;
+        result = 31 * result + id;
+        result = 31 * result + (int)getTimeBucket();
+        return result;
+    }
+
+
+    @Override public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+
+        ServiceAvgIndicator indicator = (ServiceAvgIndicator)obj;
+        if (id != indicator.id)
+            return false;
+        if (getTimeBucket() != indicator.getTimeBucket())
+            return false;
+
+        return true;
+    }
+
+
+    @Override public RemoteData.Builder serialize() {
+        RemoteData.Builder remoteBuilder = RemoteData.newBuilder();
+
+        remoteBuilder.setDataLongs(0, getTimeBucket());
+        remoteBuilder.setDataLongs(1, getSummation());
+        remoteBuilder.setDataLongs(2, getValue());
+
+
+        remoteBuilder.setDataIntegers(0, getId());
+        remoteBuilder.setDataIntegers(1, getServiceId());
+        remoteBuilder.setDataIntegers(2, getServiceInstanceId());
+        remoteBuilder.setDataIntegers(3, getCount());
+
+        return remoteBuilder;
+    }
+
+    @Override public void deserialize(RemoteData remoteData) {
+
+        setTimeBucket(remoteData.getDataLongs(0));
+        setSummation(remoteData.getDataLongs(1));
+        setValue(remoteData.getDataLongs(2));
+
+
+        setId(remoteData.getDataIntegers(0));
+        setServiceId(remoteData.getDataIntegers(1));
+        setServiceInstanceId(remoteData.getDataIntegers(2));
+        setCount(remoteData.getDataIntegers(3));
+    }
+
+    @Override public Map<String, Object> toMap() {
+        Map<String, Object> map = new HashMap<>();
+        map.put("id", getId());
+        map.put("serviceId", getServiceId());
+        map.put("serviceInstanceId", getServiceInstanceId());
+        map.put("summation", getSummation());
+        map.put("count", getCount());
+        map.put("value", getValue());
+        return map;
+    }
+
+    @Override public Indicator newOne(Map<String, Object> dbMap) {
+        ServiceAvgIndicator indicator = new ServiceAvgIndicator();
+        indicator.setId((int)dbMap.get("id"));
+        indicator.setServiceId((int)dbMap.get("serviceId"));
+        indicator.setServiceInstanceId((int)dbMap.get("serviceInstanceId"));
+        indicator.setSummation((long)dbMap.get("summation"));
+        indicator.setCount((int)dbMap.get("count"));
+        indicator.setValue((long)dbMap.get("value"));
+        return indicator;
+    }
+}
\ No newline at end of file
diff --git a/oal-parser/src/test/resources/expectedFiles/PersistentWorkerExpected.java b/oal-parser/src/test/resources/expectedFiles/PersistentWorkerExpected.java
new file mode 100644
index 0000000..81950a5
--- /dev/null
+++ b/oal-parser/src/test/resources/expectedFiles/PersistentWorkerExpected.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.oap.server.core.analysis.generated.service.serviceavg;
+
+import org.apache.skywalking.oap.server.core.analysis.worker.AbstractPersistentWorker;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+
+/**
+ * This class is auto generated. Please don't change this class manually.
+ *
+ * @author Observability Analysis Language code generator
+ */
+public class ServiceAvgPersistentWorker extends AbstractPersistentWorker<ServiceAvgIndicator> {
+
+    public ServiceAvgPersistentWorker(ModuleManager moduleManager) {
+        super(moduleManager);
+    }
+
+    @Override protected boolean needMergeDBData() {
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/oal-parser/src/test/resources/expectedFiles/RemoteWorkerExpected.java b/oal-parser/src/test/resources/expectedFiles/RemoteWorkerExpected.java
index 45e11c1..3fef183 100644
--- a/oal-parser/src/test/resources/expectedFiles/RemoteWorkerExpected.java
+++ b/oal-parser/src/test/resources/expectedFiles/RemoteWorkerExpected.java
@@ -16,7 +16,7 @@
  *
  */
 
-package org.apache.skywalking.oap.server.core.analysis.service.serviceavg;
+package org.apache.skywalking.oap.server.core.analysis.generated.service.serviceavg;
 
 import org.apache.skywalking.oap.server.core.analysis.worker.AbstractRemoteWorker;
 import org.apache.skywalking.oap.server.core.remote.selector.Selector;