So - let's do it

Generic DAO here.
Main components:
- generic DAO (above)
- generic service
- generic controller
- view & generic models
and smart developer ;)
The good approach - usage jQuery & jqgrid on frontend.
jqgrid allows us to make:
- dynamic grid
- filtering
- create, update, delete, view & search by entities
URLs:
/${appName}/${entity}/management/
/${appName}/${entity}/management/index
/${appName}/${entity}/management/insert
/${appName}/${entity}/management/update
/${appName}/${entity}/management/delete
Controllers:
- public abstract class DomainController<T extends AbstractEntity> {
- private final Log LOG = LogFactory.getLog(getClass());
- public abstract DomainService<T> getDomainService();
- /**
- * Generic main page controller
- *
- * @return ModelAndView
- */
- @RequestMapping(value = "/management", method = RequestMethod.GET)
- public ModelAndView main() {
- LOG.debug("main page");
- return new ModelAndView(getMainView());
- }
- protected abstract String getMainView();
- /**
- * Generic entity list controller (refreshing, sorting, filtering)
- *
- * @param gridViewModel
- * @param response
- * @return ResponseGridViewModel - model in JSON for grid filling
- */
- @RequestMapping(value = "/management/index", method = RequestMethod.POST)
- @ResponseBody
- public ResponseGridViewModel<T> index(@RequestBody RequestGridViewModel gridViewModel,
- HttpServletResponse response) {
- prepareGridViewModel(gridViewModel);
- return getDomainService().getFilteredEntitiy(gridViewModel);
- }
- /**
- * Process filtering criteria
- *
- * @param gridViewModel
- */
- protected void prepareGridViewModel(RequestGridViewModel gridViewModel) {
- if (!gridViewModel.getCriteria().isEmpty()) {
- Map<String, Object> criteriaMap = new HashMap<String, Object>();
- for (String key : gridViewModel.getCriteria().keySet()) {
- Object value = gridViewModel.getCriteria().get(key);
- try {
- Method m = null;
- try {
- m = getDomainService().getPersistentClass().getMethod(constructGetter(key));
- } catch (NoSuchMethodException e) {
- // it's not GET
- // let's try IS
- m = getDomainService().getPersistentClass().getMethod(constructIsGetter(key));
- }
- if (m.getReturnType().equals(value.getClass())) {
- criteriaMap.put(key, value);
- } else if (Integer.class.equals(m.getReturnType())) {
- criteriaMap.put(key, Integer.valueOf((String) value));
- } else if (Long.class.equals(m.getReturnType())) {
- criteriaMap.put(key, Long.valueOf((String) value));
- } else if (Boolean.class.equals(m.getReturnType())) {
- criteriaMap.put(key, Boolean.valueOf((String) value));
- }
- } catch (Exception e) {
- // skip method - out of the scope
- }
- }
- gridViewModel.setCriteria(criteriaMap);
- }
- }
- private String constructGetter(String strKey) {
- return "get" + WordUtils.capitalize(strKey);
- }
- private String constructIsGetter(String strKey) {
- return "is" + WordUtils.capitalize(strKey);
- }
- /**
- * Insert generic controller
- *
- * @param entity
- * @param response
- * @throws IOException
- */
- @RequestMapping(value = "/management/insert", method = RequestMethod.POST)
- protected void doInsert(@RequestBody T entity, HttpServletResponse response) throws IOException {
- entity.setId(null);
- LOG.debug("add entity : " + entity);
- // validation entity
- Map<String, String> failures = validateOnInsert(entity);
- if (!failures.isEmpty()) {
- response.getWriter().write(validationMessages(failures));
- } else {
- getDomainService().insert(entity);
- }
- }
- /**
- * Update generic controller
- *
- * @param entity
- * @param response
- * @throws IOException
- */
- @RequestMapping(value = "/management/update", method = RequestMethod.POST)
- protected void doUpdate(@RequestBody T entity, HttpServletResponse response) throws IOException {
- LOG.debug("update entity : " + entity);
- // validation entity
- Map<String, String> failures = validateOnUpdate(entity);
- if (!failures.isEmpty()) {
- response.getWriter().write(validationMessages(failures));
- } else {
- getDomainService().update(entity);
- }
- }
- /**
- * Delete generic controller
- *
- * @param entityId
- * @param response
- */
- @RequestMapping(value = "/management/delete", method = RequestMethod.POST)
- public void doDelete(@RequestParam("id") long entityId, HttpServletResponse response) {
- LOG.debug("remove item ID : " + entityId);
- getDomainService().removeEntity(entityId);
- }
- protected String validationMessages(Map<String, String> failures) {
- StringBuffer sb = new StringBuffer();
- for (String failureMsg : failures.values()) {
- if (sb.length() > 0) {
- sb.append("\",");
- }
- sb.append("\"").append(failureMsg).append("\"");
- }
- if (failures.size() > 0) {
- sb.insert(0, "{\"error\":[");
- sb.append("]}");
- }
- return sb.toString();
- }
- /**
- * Generic entity validation on insert - could be overridden
- *
- * @param entity
- * @return
- */
- protected Map<String, String> validateOnInsert(T entity) {
- return new HashMap<String, String>();
- }
- /**
- * Generic entity validation on update - could be overridden
- *
- * @param entity
- * @return
- */
- protected Map<String, String> validateOnUpdate(T entity) {
- return new HashMap<String, String>();
- }
- }
- @Controller(value = "concreteDomainController")
- @RequestMapping("/entity")
- public class ConcreteDomainController extends DomainController<Entity> {
- private String view = "entityIndex";
- private ConcreteDomainService domainService;
- public void setConcreteDomainService(ConcreteDomainService domainService) {
- this.domainService = domainService;
- }
- @Override
- public DomainService<Entity> getDomainService() {
- return domainService;
- }
- @Override
- protected String getMainView() {
- return view;
- }
- public void setView(String view) {
- this.view = view;
- }
- }
Models:
- public class RequestGridViewModel implements Serializable {
- private static final long serialVersionUID = 997675961840479859L;
- private Map<String, Object> criteria;
- private Boolean search = Boolean.FALSE;
- private Integer page = 0;
- private Integer rows = 0;
- private String order;
- private String sort;
- public RequestGridViewModel() {
- criteria = new HashMap<String, Object>();
- }
- public Integer getPage() {
- return page;
- }
- public void setPage(Integer page) {
- this.page = page;
- }
- public Integer getRows() {
- return rows;
- }
- public void setRows(Integer rows) {
- this.rows = rows;
- }
- public Boolean getSearch() {
- return search;
- }
- public void setSearch(Boolean search) {
- this.search = search;
- }
- public String getOrder() {
- return order;
- }
- public void setOrder(String order) {
- this.order = order;
- }
- public String getSort() {
- return sort;
- }
- public void setSort(String sort) {
- this.sort = sort;
- }
- public Map<String, Object> getCriteria() {
- return criteria;
- }
- public void setCriteria(Map<String, Object> criteria) {
- this.criteria = criteria;
- }
- @Override
- public String toString() {
- return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
- }
- @Override
- public int hashCode() {
- return HashCodeBuilder.reflectionHashCode(this);
- }
- @Override
- public boolean equals(Object obj) {
- return EqualsBuilder.reflectionEquals(this, obj);
- }
- }
- public class ResponseGridViewModel<T> implements Serializable {
- private static final long serialVersionUID = -2277041672814148333L;
- private List<T> gridData;
- private int currPage;
- private int totalPages;
- private int totalRecords;
- public ResponseGridViewModel() {
- }
- public ResponseGridViewModel(List<T> gridData, int currPage, int totalPages, int totalRecords) {
- this.gridData = gridData;
- this.currPage = currPage;
- this.totalPages = totalPages;
- this.totalRecords = totalRecords;
- }
- public List<T> getGridData() {
- return gridData;
- }
- public void setGridData(List<T> gridData) {
- this.gridData = gridData;
- }
- public int getCurrPage() {
- return currPage;
- }
- public void setCurrPage(int currPage) {
- this.currPage = currPage;
- }
- public int getTotalPages() {
- return totalPages;
- }
- public void setTotalPages(int totalPages) {
- this.totalPages = totalPages;
- }
- public int getTotalRecords() {
- return totalRecords;
- }
- public void setTotalRecords(int totalRecords) {
- this.totalRecords = totalRecords;
- }
- @Override
- public String toString() {
- return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
- }
- @Override
- public int hashCode() {
- return HashCodeBuilder.reflectionHashCode(this);
- }
- @Override
- public boolean equals(Object obj) {
- return EqualsBuilder.reflectionEquals(this, obj);
- }
- }
View:
- <%@ page contentType="text/html;charset=UTF-8" language="java" %>
- <html>
- <head>
- <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/css/redmond/jquery-ui-1.8.6.custom.css" />
- <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/css/ui.jqgrid.css" />
- <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/css/main.css" />
- <script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-1.4.2.min.js" ></script>
- <script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery.json-2.2.min.js" ></script>
- <script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-ui-1.8.6.custom.min.js" ></script>
- <script type="text/javascript" src="${pageContext.request.contextPath}/js/i18n/grid.locale-en.js" ></script>
- <script type="text/javascript">
- jQuery.jgrid.no_legacy_api = true;
- </script>
- <script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery.jqGrid.min.js" ></script>
- <script type="text/javascript" src="${pageContext.request.contextPath}/js/entityProcessor.js" ></script>
- </head>
- <body>
- <table id="gridContainer"></table>
- <div id="pagerGridContainer"></div>
- </body>
- </html>
entityProcessor.js :
- if (typeof (DomainProcessor) == 'undefined') {
- var DomainProcessor = {
- columns : [],
- processInsertResponse : function(response, postdata) {
- var success = true;
- var msg = "";
- if(response.responseText != "") {
- var obj = jQuery.parseJSON(response.responseText);
- if(obj != null && obj.error !== 'undefined') {
- success = false;
- msg = "<ul>";
- $.each(obj.error, function(index, value) {
- msg += "<li>" + value + "</li>";
- });
- msg += "</ul>";
- }
- }
- return [success, msg, null];
- },
- processUpdateResponse : function(response, postdata) {
- DomainProcessor.processInsertResponse(response, postdata);
- },
- init : function() {
- jQuery("#gridContainer").jqGrid({
- ajaxGridOptions : {
- type : 'POST',
- contentType : 'application/json',
- },
- serializeGridData : function(postdata) {
- if(postdata.search == true) {
- if(DomainProcessor.columns.length == 0) {
- var colModel = jQuery("#gridContainer").jqGrid('getGridParam','colModel');
- $.each(colModel, function(i, item) {
- DomainProcessor.columns.push(item.name);
- });
- }
- var transformedPostData = {};
- var objectJson = new Object;
- $.each(postdata, function(i, item) {
- if(-1 != jQuery.inArray(i, DomainProcessor.columns)) {
- if(objectJson['criteria'] == undefined) {
- objectJson['criteria'] = {};
- }
- objectJson['criteria'][i] = item;
- } else {
- objectJson[i] = item;
- }
- });
- postdata = objectJson;
- }
- return $.toJSON(postdata);
- },
- url : 'index',
- editurl : 'manipulation',
- jsonReader: {
- root : 'gridData',
- page : 'currPage',
- total : 'totalPages',
- records : 'totalRecords',
- repeatitems : false,
- },
- datatype : 'json',
- colNames : [ '#', 'Name', 'Active' ],
- colModel : [ {
- name : 'id',
- index : 'id',
- width : 20,
- key : true,
- sortable: true,
- editable: false,
- search: true
- }, {
- name : 'name',
- index : 'name',
- sortable : true,
- editable : true,
- edittype : 'text',
- editoptions : {size:10, maxlength: 10},
- editrules : {required:true},
- search: true
- }, {
- name : 'active',
- index : 'active',
- width : 80,
- editable : true,
- edittype : 'checkbox',
- editoptions : {value:"true:false"},
- search:true,
- sortable : false,
- } ],
- rowNum : 10,
- width : 700,
- height : 300,
- rowList : [ 2, 5, 10 ],
- pager : '#pagerGridContainer',
- viewrecords : true,
- caption : "Entity domain edit",
- prmNames : {
- search : "search",
- order : "order",
- oper : "action",
- sort : "sort"
- }
- }).jqGrid('filterToolbar', { searchOnEnter: true });
- jQuery("#gridContainer").jqGrid('navGrid',
- '#pagerGridContainer', {
- edit : true,
- add : true,
- view : true,
- del : true,
- search : false
- },
- // prmEdit, prmAdd, prmDel, prmSearch, prmView
- {
- closeAfterAdd: true,
- closeAfterEdit: true,
- viewPagerButtons: false,
- top: 100,
- left: 100,
- url:'update',
- afterSubmit : DomainProcessor.processInsertResponse,
- ajaxEditOptions : {
- type : 'POST',
- contentType : 'application/json',
- },
- serializeEditData: function(data) {
- return $.toJSON($.extend({}, data));
- }
- },
- {
- closeAfterAdd: true,
- closeAfterEdit: true,
- viewPagerButtons: false,
- afterSubmit : DomainProcessor.processInsertResponse,
- top: 100,
- left: 100,
- url:'insert',
- ajaxEditOptions : {
- type : 'POST',
- contentType : 'application/json',
- },
- serializeEditData: function(data) {
- return $.toJSON($.extend({}, data, {id:0}));
- }
- },
- {url:'delete'},
- {},
- {viewPagerButtons: false} // view
- );
- }
- };
- }
- jQuery(document).ready(function() {
- DomainProcessor.init();
- });
Service:
- public interface DomainService<T extends AbstractEntity> {
- ResponseGridViewModel<T> getFilteredEntitiy(RequestGridViewModel gridViewModel);
- void removeEntity(Long entityId);
- void insert(T entity);
- void update(T entity);
- Class<T> getPersistentClass();
- }
- public class DomainServiceImpl<T extends AbstractEntity> implements DomainService<T> {
- private final Log LOG = LogFactory.getLog(getClass());
- private Class<T> persistentClass;
- private GenericRepository<T> rep;
- public void setGenericRepository(GenericRepository<T> rep) {
- this.rep = rep;
- }
- @Override
- public Class<T> getPersistentClass() {
- return persistentClass;
- }
- @SuppressWarnings("unchecked")
- public DomainServiceImpl() {
- this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
- .getActualTypeArguments()[0];
- }
- @Override
- public ResponseGridViewModel<T> getFilteredEntitiy(RequestGridViewModel gridViewModel) {
- ResponseGridViewModel<T> gridResponse = new ResponseGridViewModel<T>();
- Map<String, Object> crits = new HashMap<String, Object>();
- int maxResults = gridViewModel.getRows();
- int firstResult = ((gridViewModel.getPage() - 1) * maxResults);
- // criterias
- if (gridViewModel.getSearch() && !gridViewModel.getCriteria().isEmpty()) {
- crits.putAll(gridViewModel.getCriteria());
- }
- if(!gridViewModel.getCriteria().containsKey("active")) {
- crits.put("active", Boolean.TRUE);
- }
- // ordering
- String orderByField = (StringUtils.isEmpty(gridViewModel.getSort())) ? "id" : gridViewModel.getSort();
- OrderBy orderBy = ("desc".equals(gridViewModel.getOrder())) ? OrderBy.desc(orderByField) : OrderBy.asc(orderByField);
- List<T> entities = rep.findByCriteria(getPersistentClass(), crits, firstResult, maxResults, orderBy);
- gridResponse.setGridData(entities);
- // page counting
- Integer totalRecords = rep.findByCriteria(getPersistentClass(), crits);
- int totalPages = (totalRecords / maxResults) + (((totalRecords % maxResults) == 0) ? 0 : 1);
- gridResponse.setTotalPages(totalPages);
- gridResponse.setTotalRecords(totalRecords);
- int currentPage = gridViewModel.getPage();
- if (currentPage > totalPages) {
- currentPage = 1;
- }
- if (totalPages == 1) {
- currentPage = 1;
- }
- if (totalPages == 0) {
- currentPage = 0;
- }
- gridResponse.setCurrPage(currentPage);
- return gridResponse;
- }
- @Override
- public void removeEntity(Long entityId) {
- LOG.debug("remove entity ID : " + entityId);
- T entity = rep.findById(getPersistentClass(), entityId);
- if (entity != null) {
- rep.remove(entity);
- }
- }
- @Override
- public void insert(T entity) {
- rep.add(entity);
- }
- @Override
- public void update(T entity) {
- rep.update(entity);
- }
- }
- public interface EntityDomainService extends DomainService<Entity> {
- }
- @Service
- public class EntityDomainServiceImpl extends DomainServiceImpl<Entity> implements EntityDomainService {
- }
I hope - this story helps you :)
1 comment:
Thanks for share your code with us.
Could you send or post your code (this app example)?
Post a Comment