Telerik Kendo UIの件【3】GridView MVVMフロントバックグラウンド連動サービス側ページング、パケット化、フィルタリング(クエリー)、ソート


中国文化の後、kendo uiコンポーネントを具体的に使用し始めました.よくシステムを開発する私は、一般的に最もよく使われるコントロールから使います.それはテーブルコントロールGridViewです.現在のソフトウェアシステムは基本的にラベルボックス、テキストボックス、選択ボックス、ツリー、テーブルが積み上げられています.そのため、UIコンポーネントに触れるとき、私はいつもGridView、テーブルコントロールに最も注目しています.小さなテーブルコントロールは、フロントエンドのプレゼンテーションと使用に良い感じを与えると同時に、開発の作業量を大幅に削減することができます.
Kendo UIはこの方面で私をとても満足させて、甚だしきに至ってはショックを受けました.GridViewは、プレゼンテーションレイヤでデータのフィルタリング、ソート、パケットを簡単に行うだけでなく、簡単な構成でインタフェースのフィルタリング、ソート、パケットをバックグラウンドに直接連動させることができるからです.表示されるデータ列であれば、クエリーする場合は、テーブルの上にラベル選択領域を作成する必要はありません.もちろんこれもソフトウェア開発モデルに対する大きな挑戦であり、ラベルボックス、選択ボックスなどのコントロールの山を通じて条件を指定し、where条件を生成することに慣れている.そして、ポイントクエリーを行うとき、バックグラウンドクエリーを再提示する開発方式は、完全に時代遅れであり、効率が悪いだけでなく、開発に多くの時間を浪費し、ユーザーが使用するときも直感的ではない.クエリーの結果に及ぼす条件の影響については、ユーザーがまったく知らないため、クエリーの条件が大量に設定されているため、クエリーの結果がない場合に怒っている場合があります.
二、直接連動、sqlなし、パッチワークなし、クエリー提示.
kendo uiのGridViewコントロールは、MVVMでデータソースをバインドし、REST-FullスタイルのURLでspring mvcのバックグラウンドと通信することで、簡単なGridViewを使用することで、バックグラウンドデータのリアルタイムクエリー、修正、増加、削除を直接実現することができます.また、sqlやhqlを書く必要はなく、nosqlを組み立てる条件も必要ありません.すべては完璧な連動です.インタフェースの操作は条件オブジェクトを生成し、フレームワークシステム全体が渾然一体となり、バックエンドhibernateはフロントエンドオブジェクトからフィードバックされた条件データを自動的に組み立て、結果をクエリーします.
こんなにたくさん言って、多くの人が見てめまいがするかもしれませんが、コードほど爽やかではありません.ではkendo UIのGridViewの美しさを教えていただきましょう!
ページの頭は無視して、headの前の文章を見てみましょう.bodyの部分は、以下のように書けばGridViewが使えます.
ヘッドはまずREST-FullスタイルのCURD接続定義を追加します(個人的な習慣で、エンジニアリング管理が便利で、jsに直接書くこともできます)
<c:url value="/kendo/test6.json" var="transportReadUrl" />
<c:url value="/kendo/test6c.json" var="transportCreateUrl" />
<c:url value="/kendo/test6u.json" var="transportUpdateUrl" />
<c:url value="/kendo/test6d.json" var="transportDestroyUrl" />

次はbodyセクションです.
	<div>
				<div id="grid" style="height:580px"></div>
	</div>
	
	<script>
		$(document).ready(function() {
			$("#grid").kendoGrid({
				"columnMenu" : true,
				"dataSource" : {
					"schema" : {
						"total" : "total",
						"model" : {
							"id" : "objectId",
							"fields" : {
								"objectId" : {
									"type" : "number"
								},
								"age" : {
									"type" : "number"
								},
								"name" : {
									"type" : "string"
								},
								"bdate" : {
									"type" : "date"
								}
							}
						},
						"data" : "data",
						"groups" : "data"
					},
					"serverFiltering" : true,
					"transport" : {
						"destroy" : {
							"dataType" : "json",
							"contentType" : "application/json",
							"type" : "POST",
							"url" : "${transportDestroyUrl}"
						},
						"update" : {
							"dataType" : "json",
							"contentType" : "application/json",
							"type" : "POST",
							"url" : "${transportUpdateUrl}"
						},
						"read" : {
							"dataType" : "json",
							"contentType" : "application/json",
							"type" : "POST",
							"url" : "${transportReadUrl}"
						},
						"create" : {
							"dataType" : "json",
							"contentType" : "application/json",
							"type" : "POST",
							"url" : "${transportCreateUrl}"
						},
						"parameterMap" : function(options) {
							return JSON.stringify(options);
						}
					},
					"batch" : false,
					"serverSorting" : true,
					"pageSize" : 10.0,
					"serverPaging" : true,
					"serverGrouping" : true
				},
				"toolbar" : [ {
					"text" : "    ",
					"name" : "create"
				} ],
				"reorderable" : true,
				"filterable" : true,
				"pageable" : {
					"input" : true,
					"buttonCount" : 5.0,
					"pageSize" : 10.0,
					"pageSizes" : [ 5, 10, 15, 20, 30 ],
					"refresh" : true
				},
				"sortable" : true,
				"columns" : [ {
					"field" : "objectId",
					"title" : "  ",
					"hidden" : true
				}, {
					"field" : "name",
					"title" : "  "
				}, {
					"field" : "age",
					"title" : "  "
				}, {
					"field" : "bdate",
					"title" : "    ",
					"filterable" : {
						"ui" : "datetimepicker"
					},
					"format" : "{0:yyyy-MM-dd HH:mm:ss}"
				}, {
					"title" : " ",
					"command" : [ {
						"text" : "  ",
						"name" : "edit"
					}, {
						"text" : "  ",
						"name" : "destroy"
					} ]
				} ],
				"groupable" : true,
				"editable" : {
					"mode" : "inline"
				}
			});
		});
	</script>

次に、バックグラウンドspring MVC制御層は以下の通りである.
@Controller
public class KendoControl {

	@Autowired
	private SessionFactory sessionFactory;



	@RequestMapping(value = "/kendo/test6d", method = RequestMethod.POST)
	public TestObject delete(@RequestBody TestObject model) {
		System.out.println("delete Object-->" + model);
		sessionFactory.getCurrentSession().delete(model);
		return model;
	}

	@RequestMapping(value = "/kendo/test6u", method = RequestMethod.POST)
	public TestObject update(@RequestBody TestObject model) {
		System.out.println("update mapObject-->" + model);
		Session session = sessionFactory.getCurrentSession();
		session.saveOrUpdate(model);
		session.flush();
		return model;
	}

	@RequestMapping(value = "/kendo/test6c", method = RequestMethod.POST)
	public TestObject create(@RequestBody TestObject model) {
		System.out.println("create mapObject-->" + model);
		Session session = sessionFactory.getCurrentSession();
		session.saveOrUpdate(model);
		session.flush();
		return model;
	}

	@RequestMapping(value = "/kendo/test6", method = RequestMethod.POST)
	public DataSourceResult kendoTest6(
			@RequestBody com.kendoui.spring.models.DataSourceRequest request) {
		System.out.println("RequestBody-->" + request.toString());
		return request.toDataSourceResult(sessionFactory.getCurrentSession(),
				TestObject.class);
	}

}

皆さんは間違いなく発見しました.中にはDataSourceRequestクラスとDataSourceResultクラスがあります.すべてのクエリーはRequestに関連しています.コアの実現はすでにモードを形成しています.モデルを通じてインタフェースクエリーとバックエンドクエリーのマッピングが完了しています.そのため、インタフェースがフィルタリング、グループ化、ソートなどの操作を選択すると、read urlが呼び出されます.パラメータはspringのmvc制御層に伝達され、データSourceRequestオブジェクトに逆シーケンス化され、オブジェクトはすべての操作を実現し、sqlを組み立てる必要はありません.偉大で美しいでしょう.この2つのクラスはkendo ui for JSPのDEMOエンジニアリングから直接cpで使用され,日付フォーマットの問題でDataSourceRequestクラスが変更された.ソースコードを次に示します!
エンティティクラスTestObject:
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;


/**
 * TestObject entity. @author MyEclipse Persistence Tools
 */
@Entity
@Table(name="test_object")
public class TestObject  implements java.io.Serializable {


    // Fields    

     private Long objectId;
     private String name;
     private Integer age;
     private Date bdate;


    // Constructors



	/** default constructor */
    public TestObject() {
    }

	/** minimal constructor */
    public TestObject(Long id) {
        this.objectId = id;
    }
    
    /** full constructor */
    public TestObject(Long id, String name, Integer age) {
        this.objectId = id;
        this.name = name;
        this.age = age;
    }

   
    // Property accessors
    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="id")

    public Long getObjectId() {
        return this.objectId;
    }
    
    public void setObjectId(Long id) {
        this.objectId = id;
    }
    
    @Column(name="name")

    public String getName() {
        return this.name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    @Column(name="age")

    public Integer getAge() {
        return this.age;
    }
    
    public void setAge(Integer age) {
        this.age = age;
    }
    
	@Temporal(TemporalType.TIMESTAMP)
    @Column(name="bdate", length = 11)
    
    public Date getBdate() {
		return bdate;
	}

	public void setBdate(Date bdate) {
		this.bdate = bdate;
	}

	@Override
	public String toString() {
		return "TestObject [objectId=" + objectId + ", name=" + name + ", age="
				+ age + ", bdate=" + bdate + "]";
	}







}

DataSourceRequest:
package com.kendoui.spring.models;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.codehaus.jackson.annotate.JsonAnySetter;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Junction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.SimpleExpression;
import org.hibernate.transform.ResultTransformer;

public class DataSourceRequest {
	private int page;
	private int pageSize;
	private int take;
	private int skip;
	private List<SortDescriptor> sort;
	private List<GroupDescriptor> group;
	private List<AggregateDescriptor> aggregate;
	private HashMap<String, Object> data;

	private FilterDescriptor filter;

	public DataSourceRequest() {
		filter = new FilterDescriptor();
		data = new HashMap<String, Object>();
	}

	public HashMap<String, Object> getData() {
		return data;
	}

	@JsonAnySetter
	public void handleUnknown(String key, Object value) {
		data.put(key, value);
	}

	public int getPage() {
		return page;
	}

	public void setPage(int page) {
		this.page = page;
	}

	public int getPageSize() {
		return pageSize;
	}

	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}

	public int getTake() {
		return take;
	}

	public void setTake(int take) {
		this.take = take;
	}

	public int getSkip() {
		return skip;
	}

	public void setSkip(int skip) {
		this.skip = skip;
	}

	public List<SortDescriptor> getSort() {
		return sort;
	}

	public void setSort(List<SortDescriptor> sort) {
		this.sort = sort;
	}

	public FilterDescriptor getFilter() {
		return filter;
	}

	public void setFilter(FilterDescriptor filter) {
		this.filter = filter;
	}

	private static void restrict(Junction junction, FilterDescriptor filter,
			Class<?> clazz) {
		String operator = filter.getOperator();
		String field = filter.getField();
		Object value = filter.getValue();
		boolean ignoreCase = filter.isIgnoreCase();

		try {
			Class<?> type = new PropertyDescriptor(field, clazz)
					.getPropertyType();
			if (type == double.class || type == Double.class) {
				value = Double.parseDouble(value.toString());
			} else if (type == float.class || type == Float.class) {
				value = Float.parseFloat(value.toString());
			} else if (type == long.class || type == Long.class) {
				value = Long.parseLong(value.toString());
			} else if (type == int.class || type == Integer.class) {
				value = Integer.parseInt(value.toString());
			} else if (type == short.class || type == Short.class) {
				value = Short.parseShort(value.toString());
			} else if (type == boolean.class || type == Boolean.class) {
				value = Boolean.parseBoolean(value.toString());
			} else if (type == Date.class) {
				SimpleDateFormat df = new SimpleDateFormat(
						"yyyy-MM-dd HH:mm:ss");

				String input = value.toString();
				value = df.parse(input);
			}
		} catch (IntrospectionException e) {
		} catch (NumberFormatException nfe) {
		} catch (ParseException e) {
		}

		switch (operator) {
		case "eq":
			if (value instanceof String) {
				junction.add(Restrictions.ilike(field, value.toString(),
						MatchMode.EXACT));
			} else {
				junction.add(Restrictions.eq(field, value));
			}
			break;
		case "neq":
			if (value instanceof String) {
				junction.add(Restrictions.not(Restrictions.ilike(field,
						value.toString(), MatchMode.EXACT)));
			} else {
				junction.add(Restrictions.ne(field, value));
			}
			break;
		case "gt":
			junction.add(Restrictions.gt(field, value));
			break;
		case "gte":
			junction.add(Restrictions.ge(field, value));
			break;
		case "lt":
			junction.add(Restrictions.lt(field, value));
			break;
		case "lte":
			junction.add(Restrictions.le(field, value));
			break;
		case "startswith":
			junction.add(getLikeExpression(field, value.toString(),
					MatchMode.START, ignoreCase));
			break;
		case "endswith":
			junction.add(getLikeExpression(field, value.toString(),
					MatchMode.END, ignoreCase));
			break;
		case "contains":
			junction.add(getLikeExpression(field, value.toString(),
					MatchMode.ANYWHERE, ignoreCase));
			break;
		case "doesnotcontain":
			junction.add(Restrictions.not(Restrictions.ilike(field,
					value.toString(), MatchMode.ANYWHERE)));
			break;
		}

	}

	private static Criterion getLikeExpression(String field, String value,
			MatchMode mode, boolean ignoreCase) {
		SimpleExpression expression = Restrictions.like(field, value, mode);

		if (ignoreCase == true) {
			expression = expression.ignoreCase();
		}

		return expression;
	}

	private static void filter(Criteria criteria, FilterDescriptor filter,
			Class<?> clazz) {
		if (filter != null) {
			List<FilterDescriptor> filters = filter.filters;

			if (!filters.isEmpty()) {
				Junction junction = Restrictions.conjunction();

				if (!filter.getFilters().isEmpty()
						&& filter.getLogic().toString().equals("or")) {
					junction = Restrictions.disjunction();
				}

				for (FilterDescriptor entry : filters) {
					if (!entry.getFilters().isEmpty()) {
						filter(criteria, entry, clazz);
					} else {
						restrict(junction, entry, clazz);
					}
				}

				criteria.add(junction);
			}
		}
	}

	private static void sort(Criteria criteria, List<SortDescriptor> sort) {
		if (sort != null && !sort.isEmpty()) {
			for (SortDescriptor entry : sort) {
				String field = entry.getField();
				String dir = entry.getDir();

				if (dir.equals("asc")) {
					criteria.addOrder(Order.asc(field));
				} else if (dir.equals("desc")) {
					criteria.addOrder(Order.desc(field));
				}
			}
		}
	}

	private List<?> groupBy(List<?> items, List<GroupDescriptor> group,
			Class<?> clazz, final Session session,
			List<SimpleExpression> parentRestrictions)
			throws IntrospectionException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException {
		ArrayList<Map<String, Object>> result = new ArrayList<Map<String, Object>>();

		if (!items.isEmpty() && group != null && !group.isEmpty()) {
			List<List<SimpleExpression>> restrictions = new ArrayList<List<SimpleExpression>>();

			GroupDescriptor descriptor = group.get(0);
			List<AggregateDescriptor> aggregates = descriptor.getAggregates();

			final String field = descriptor.getField();

			Method accessor = new PropertyDescriptor(field, clazz)
					.getReadMethod();

			Object groupValue = accessor.invoke(items.get(0));

			List<Object> groupItems = createGroupItem(group.size() > 1, clazz,
					session, result, aggregates, field, groupValue,
					parentRestrictions);

			List<SimpleExpression> groupRestriction = new ArrayList<SimpleExpression>(
					parentRestrictions);
			groupRestriction.add(Restrictions.eq(field, groupValue));
			restrictions.add(groupRestriction);

			for (Object item : items) {
				Object currentValue = accessor.invoke(item);

				if (!groupValue.equals(currentValue)) {
					groupValue = currentValue;
					groupItems = createGroupItem(group.size() > 1, clazz,
							session, result, aggregates, field, groupValue,
							parentRestrictions);

					groupRestriction = new ArrayList<SimpleExpression>(
							parentRestrictions);
					groupRestriction.add(Restrictions.eq(field, groupValue));
					restrictions.add(groupRestriction);
				}
				groupItems.add(item);
			}

			if (group.size() > 1) {
				Integer counter = 0;
				for (Map<String, Object> g : result) {
					g.put("items",
							groupBy((List<?>) g.get("items"),
									group.subList(1, group.size()), clazz,
									session, restrictions.get(counter++)));
				}
			}
		}

		return result;
	}

	private List<Object> createGroupItem(Boolean hasSubgroups, Class<?> clazz,
			final Session session, ArrayList<Map<String, Object>> result,
			List<AggregateDescriptor> aggregates, final String field,
			Object groupValue, List<SimpleExpression> aggregateRestrictions) {

		Map<String, Object> groupItem = new HashMap<String, Object>();
		List<Object> groupItems = new ArrayList<Object>();

		result.add(groupItem);

		if (groupValue instanceof Date) { // format date
			SimpleDateFormat formatter = new SimpleDateFormat(
					"yyyy-MM-dd HH:mm:ss");
			String formattedDate = formatter.format(((Date) groupValue)
					.getTime());
			groupItem.put("value", formattedDate);
		} else {
			groupItem.put("value", groupValue);
		}

		groupItem.put("field", field);
		groupItem.put("hasSubgroups", hasSubgroups);

		if (aggregates != null && !aggregates.isEmpty()) {
			Criteria criteria = session.createCriteria(clazz);

			filter(criteria, getFilter(), clazz); // filter the set by the
													// selected criteria

			SimpleExpression currentRestriction = Restrictions.eq(field,
					groupValue);

			if (aggregateRestrictions != null
					&& !aggregateRestrictions.isEmpty()) {
				for (SimpleExpression simpleExpression : aggregateRestrictions) {
					criteria.add(simpleExpression);
				}
			}
			criteria.add(currentRestriction);

			groupItem.put("aggregates",
					calculateAggregates(criteria, aggregates));
		} else {
			groupItem.put("aggregates", new HashMap<String, Object>());
		}
		groupItem.put("items", groupItems);
		return groupItems;
	}

	@SuppressWarnings({ "serial", "unchecked" })
	private static Map<String, Object> calculateAggregates(Criteria criteria,
			List<AggregateDescriptor> aggregates) {
		return (Map<String, Object>) criteria
				.setProjection(createAggregatesProjection(aggregates))
				.setResultTransformer(new ResultTransformer() {
					@Override
					public Object transformTuple(Object[] value,
							String[] aliases) {
						Map<String, Object> result = new HashMap<String, Object>();

						for (int i = 0; i < aliases.length; i++) {
							String alias = aliases[i];
							Map<String, Object> aggregate;

							String name = alias.split("_")[0];
							if (result.containsKey(name)) {
								((Map<String, Object>) result.get(name)).put(
										alias.split("_")[1], value[i]);
							} else {
								aggregate = new HashMap<String, Object>();
								aggregate.put(alias.split("_")[1], value[i]);
								result.put(name, aggregate);
							}
						}

						return result;
					}

					@SuppressWarnings("rawtypes")
					@Override
					public List transformList(List collection) {
						return collection;
					}
				}).list().get(0);
	}

	private static ProjectionList createAggregatesProjection(
			List<AggregateDescriptor> aggregates) {
		ProjectionList projections = Projections.projectionList();
		for (AggregateDescriptor aggregate : aggregates) {
			String alias = aggregate.getField() + "_"
					+ aggregate.getAggregate();
			if (aggregate.getAggregate().equals("count")) {
				projections.add(Projections.count(aggregate.getField()), alias);
			} else if (aggregate.getAggregate().equals("sum")) {
				projections.add(Projections.sum(aggregate.getField()), alias);
			} else if (aggregate.getAggregate().equals("average")) {
				projections.add(Projections.avg(aggregate.getField()), alias);
			} else if (aggregate.getAggregate().equals("min")) {
				projections.add(Projections.min(aggregate.getField()), alias);
			} else if (aggregate.getAggregate().equals("max")) {
				projections.add(Projections.max(aggregate.getField()), alias);
			}
		}
		return projections;
	}

	private List<?> group(final Criteria criteria, final Session session,
			final Class<?> clazz) {
		List<?> result = new ArrayList<Object>();
		List<GroupDescriptor> group = getGroup();

		if (group != null && !group.isEmpty()) {
			try {
				result = groupBy(criteria.list(), group, clazz, session,
						new ArrayList<SimpleExpression>());
			} catch (IllegalAccessException | IllegalArgumentException
					| InvocationTargetException | HibernateException
					| IntrospectionException e) {

				e.printStackTrace();
			}
		}
		return result;
	}

	private static long total(Criteria criteria) {
		long total = Long.parseLong(criteria
				.setProjection(Projections.rowCount()).uniqueResult()
				.toString());

		criteria.setProjection(null);
		criteria.setResultTransformer(Criteria.ROOT_ENTITY);

		return total;
	}

	private static void page(Criteria criteria, int take, int skip) {
		criteria.setMaxResults(take);
		criteria.setFirstResult(skip);
	}

	public DataSourceResult toDataSourceResult(Session session, Class<?> clazz) {
		Criteria criteria = session.createCriteria(clazz);

		filter(criteria, getFilter(), clazz);

		long total = total(criteria);

		sort(criteria, sortDescriptors());

		page(criteria, getTake(), getSkip());

		DataSourceResult result = new DataSourceResult();

		result.setTotal(total);

		List<GroupDescriptor> groups = getGroup();

		if (groups != null && !groups.isEmpty()) {
			result.setData(group(criteria, session, clazz));
		} else {
			result.setData(criteria.list());
		}

		List<AggregateDescriptor> aggregates = getAggregate();
		if (aggregates != null && !aggregates.isEmpty()) {
			result.setAggregates(aggregate(aggregates, getFilter(), session,
					clazz));
		}

		return result;
	}

	private static Map<String, Object> aggregate(
			List<AggregateDescriptor> aggregates, FilterDescriptor filters,
			Session session, Class<?> clazz) {
		Criteria criteria = session.createCriteria(clazz);

		filter(criteria, filters, clazz);

		return calculateAggregates(criteria, aggregates);
	}

	private List<SortDescriptor> sortDescriptors() {
		List<SortDescriptor> sort = new ArrayList<SortDescriptor>();

		List<GroupDescriptor> groups = getGroup();
		List<SortDescriptor> sorts = getSort();

		if (groups != null) {
			sort.addAll(groups);
		}

		if (sorts != null) {
			sort.addAll(sorts);
		}
		return sort;
	}

	public List<GroupDescriptor> getGroup() {
		return group;
	}

	public void setGroup(List<GroupDescriptor> group) {
		this.group = group;
	}

	public List<AggregateDescriptor> getAggregate() {
		return aggregate;
	}

	public void setAggregate(List<AggregateDescriptor> aggregate) {
		this.aggregate = aggregate;
	}

	public static class SortDescriptor {
		private String field;
		private String dir;

		public String getField() {
			return field;
		}

		public void setField(String field) {
			this.field = field;
		}

		public String getDir() {
			return dir;
		}

		public void setDir(String dir) {
			this.dir = dir;
		}

		@Override
		public String toString() {
			return "SortDescriptor [field=" + field + ", dir=" + dir + "]";
		}

	}

	public static class GroupDescriptor extends SortDescriptor {
		private List<AggregateDescriptor> aggregates;

		public GroupDescriptor() {
			aggregates = new ArrayList<AggregateDescriptor>();
		}

		public List<AggregateDescriptor> getAggregates() {
			return aggregates;
		}

		@Override
		public String toString() {
			return "GroupDescriptor [aggregates=" + aggregates + "]";
		}

	}

	public static class AggregateDescriptor {
		private String field;
		private String aggregate;

		public String getField() {
			return field;
		}

		public void setField(String field) {
			this.field = field;
		}

		public String getAggregate() {
			return aggregate;
		}

		public void setAggregate(String aggregate) {
			this.aggregate = aggregate;
		}

		@Override
		public String toString() {
			return "AggregateDescriptor [field=" + field + ", aggregate="
					+ aggregate + "]";
		}

	}

	public static class FilterDescriptor {
		private String logic;
		private List<FilterDescriptor> filters;
		private String field;
		private Object value;
		private String operator;
		private boolean ignoreCase = true;

		public FilterDescriptor() {
			filters = new ArrayList<FilterDescriptor>();
		}

		public String getField() {
			return field;
		}

		public void setField(String field) {
			this.field = field;
		}

		public Object getValue() {
			return value;
		}

		public void setValue(Object value) {
			this.value = value;
		}

		public String getOperator() {
			return operator;
		}

		public void setOperator(String operator) {
			this.operator = operator;
		}

		public String getLogic() {
			return logic;
		}

		public void setLogic(String logic) {
			this.logic = logic;
		}

		public boolean isIgnoreCase() {
			return ignoreCase;
		}

		public void setIgnoreCase(boolean ignoreCase) {
			this.ignoreCase = ignoreCase;
		}

		public List<FilterDescriptor> getFilters() {
			return filters;
		}

		@Override
		public String toString() {
			return "FilterDescriptor [logic=" + logic + ", filters=" + filters
					+ ", field=" + field + ", value=" + value + ", operator="
					+ operator + ", ignoreCase=" + ignoreCase + "]";
		}

	}

	@Override
	public String toString() {
		return "DataSourceRequest [page=" + page + ", pageSize=" + pageSize
				+ ", take=" + take + ", skip=" + skip + ", sort=" + sort
				+ ", group=" + group + ", aggregate=" + aggregate + ", data="
				+ data + ", filter=" + filter + "]";
	}

}

DataSourceResult:
package com.kendoui.spring.models;

import java.util.List;
import java.util.Map;

public class DataSourceResult {
    private long total;
    
    private List<?> data;
    
    private Map<String, Object> aggregates;

    public long getTotal() {
        return total;
    }

    public void setTotal(long total) {
        this.total = total;
    }

    public List<?> getData() {
        return data;
    }

    public void setData(List<?> data) {
        this.data = data;
    }

    public Map<String, Object> getAggregates() {
        return aggregates;
    }

    public void setAggregates(Map<String, Object> aggregates) {
        this.aggregates = aggregates;
    }
}

どうですか、プロフィールで美しいでしょう.これがデザインの力です.多くの人、仕事の仲間はすべて設計を無視して、設計の無用論はとても盛んで、何が設計の時間があるならば、私はとっくにどのようにします.彼らは勤勉すぎて、仕事をしている間に頭を働かさず、疲れているのは永遠に自分だとしか言いようがありません.デザインの美しさはプログラマーを完璧にサボることができて、骨の中に怠け者で、怠け者は聡明な源泉で、デザインの動力です.
ここでは細部を強調しなければならない.主にkendo ui GridViewの使用の詳細であり、CSDNの別のブロガーリード蜂が説明していないものでもある.
1、dataSourceのtransportメンバーのparameterMapメソッドは、バックグラウンドspringのmvcマッピングと密接に関連する上書きを行う必要があります.parameterMapメソッドを書き換えると、バックグラウンドにデータをコミットするときにjsonオブジェクトをコミットし、パラメータが正常に逆シーケンス化されます.覚えておいて!
						"parameterMap" : function(options) {
							return JSON.stringify(options);
						}

2、dataSourceのschemaメンバーのmodelメンバーは、id属性を定義し、エンティティのIDを指す必要があります.そうでないと、コントロール自体の変更、削除、追加操作が混乱し、正常に動作しません.増加記録が2つ増え、修正記録も多く記録される現象だ.コントロールは修正、追加された操作を正常に認識できないため、どのような記録が対象なのか.また、キャンセルボタンは、削除操作が増加または修正中にキャンセルされ、削除された問題が記録されると認識されます.そのため、モデルメンバーのid属性には必ず対応するidを指定しなければならないことに注意してください.そうしないと、GridViewはクエリー操作しかできず、追加、削除、変更はできません.
"dataSource" : {
					"schema" : {
						"total" : "total",
						"model" : {
							"id" : "objectId",
							"fields" : {
								"objectId" : {
									"type" : "number"
								},
								"age" : {
									"type" : "number"
								},
								"name" : {
									"type" : "string"
								},
								"bdate" : {
									"type" : "date"
								}
							}
						},

3、GridView自身の以下の属性を設定することに注意して、サービス側のページング、サービス側のクエリー、サービス側のフィルタリング、サービス側のグループ化、つまり私が言ったインタフェースとバックグラウンドの直接的な連動を確保します.データをインタフェースにクエリーしてバッファリングした後ではなく、インタフェースからページを分け、フィルタリングし、グループ化します.
					"serverSorting" : true,
				"serverPaging" : true,
					"serverGrouping" : true

4、日付フィールドの説明設定に注意してください.次のコードの設定により、日付フィールドのフィルタ設定がカレンダーコントロールに変わります.操作しやすい!
{
					"field" : "bdate",
					"title" : "    ",
					"filterable" : {
						"ui" : "datetimepicker"
					},
					"format" : "{0:yyyy-MM-dd HH:mm:ss}"
				}

5、最後の列に表示されるボタンの設定に注意してください.ここでnameフィールドの属性値は固定されており、3種類しかありません.textは外観表示の中国語で、設定しないと英語になります.
{
					"title" : " ",
					"command" : [ {
						"text" : "  ",
						"name" : "edit"
					}, {
						"text" : "  ",
						"name" : "destroy"
					} ]
				} 

6、コードの最後の部分.直接行で編集するか、ポップアップウィンドウで編集するかを指定します.ポップアップウィンドウでinlineをpopupに変更すればよいので、個人的にはpopupを推奨します.popupモードでは、ポップアップウィンドウの属性がすべての属性であり、リストhiddenがtrueに設定された属性もポップアップウィンドウに表示されます.これにより、リストの表示が簡潔で、編集時にフィールド属性が欠けない目的を達成できます.
"editable" : {
					"mode" : "inline"
				}

7、列幅を調整したい場合はresizeableをtrueに設定しますが、真に設定する場合は、すべての列にwidthプロパティを指定する必要があります.そうしないと、widthを指定していない列は表示できません.hiddenから表示になると、表示できなくなります!
8、GridView上部のToolbarツールバーは、最後の列の2つのボタンの構成と同様に設定されており、自分で中国語を指定する必要があります.
				"toolbar" : [ {
					"text" : "    ",
					"name" : "create"
				} ],

楽屋の2つの肝心な類については、その山の石は玉を攻めることができて、取って直接使うことができます!良いモデルパッケージで、これ以上何も変更する必要はありません.動くのは私たちのバックグラウンド設計の実現です.また、私のクエリーは複雑なクエリーで、マルチテーブルが結合しているので、複雑なsqlを書かなければならないという人もいるかもしれません.私はただ、それを共通のsqlにしてビューを形成し、ビューをhibernateエンティティにマッピングし、GridViewに直接バインドすることを考えることができますか?多くのことを省くことができます!事は人為にある!設計の力は私たちに多くのことを考えさせて、極めて少量の消費電力で、極めて美しい効果を実現することができます!
未完待機・・・