Статьи

Рекомендации Java Coding: лучшая реализация поиска

В веб-приложениях поиск информации на основе выбранных критериев и отображение результатов является очень распространенным требованием. Предположим, нам нужно искать пользователей по их имени. Конечный пользователь введет имя пользователя в текстовое поле и нажмет кнопку поиска, и результаты пользователя будут выбраны из базы данных и отображены в сетке.

Сначала это выглядит просто, и мы можем начать реализовывать это следующим образом:
 

public class UserSearchAction extends Action
{
public ActionForward execute(...)
{
SearchForm sf = (SearchForm)form;
String searchName = sf.getSearchName();
UserService userService = new UserService();
List<User> searchResults = userService.search(searchName);
//put search results in request and dsplay in JSP
}

}

public class UserService
{
public List<User> search(String username)
{
// query the DB and get the results by applying filter on USERNAME column
List<User> users = UserDAO.search(username);

}
}

Вышеуказанная реализация отлично работает для текущего требования.

Позже клиент хочет отобразить только 10 строк на странице и отобразить сообщение типа «Отображение 1-10 из 35 пользователей».

Теперь необходимо изменить код для запроса на изменение.

 

public class UserSearchAction extends Action
{
public ActionForward execute(...)
{
SearchForm sf = (SearchForm)form;
String searchName = sf.getSearchName();
UserService userService = new UserService();
Map<String, Object> searchResultsMap = userService.search(searchName, start, pageSize);
List<User> users = (List<User>)searchResultsMap.get("DATA");
Integer count = (Integer)searchResultsMap.get("COUNT");
//put search results in request and dsplay in JSP
}

}

 

public class UserService
{
public Map<String, Object> search(String username, int start, int pageSize)
{
//Get the total number of results for this criteria int count = UserDAO.searchResultsCount(username);
List<User> users = UserDAO.search(username, start, pageSize);
// query the DB and get the start to start+pageSize results by applying filter on USERNAME column
Map<String, Object> RESULTS_MAP = new HashMap<String, Object>();
RESULTS_MAP.put("DATA",users);
RESULTS_MAP.put("COUNT",count);
return RESULTS_MAP;
}
}

Позже клиент хочет предоставить конечному пользователю возможность выбрать тип поиска по идентификатору пользователя или по имени пользователя и отобразить разбитые на страницы результаты.
Теперь снова необходимо изменить код для запроса на изменение.
 

public class UserSearchAction extends Action
{
public ActionForward execute(...)
{
SearchForm sf = (SearchForm)form;
String searchName = sf.getSearchName();
String searchId = sf.getSearchId();
UserService userService = new UserService();
Map<String, Object> searchCriteriaMap = new HashMap<String, Object>();
//searchCriteriaMap.put("SEARCH_BY","NAME");
searchCriteriaMap.put("SEARCH_BY","ID");
searchCriteriaMap.put("ID",searchId);
searchCriteriaMap.put("START",start);
searchCriteriaMap.put("PAGESIZE",pageSize);

Map<String, Object> searchResultsMap = userService.search(searchCriteriaMap);
List<User> users = (List<User>)searchResultsMap.get("DATA");
Integer count = (Integer)searchResultsMap.get("COUNT");
//put search results in request and dsplay in JSP
}

}

 

public class UserService
{
public Map<String, Object> search(Map<String, Object> searchCriteriaMap)
{
return UserDAO.search(searchCriteriaMap);
}
}

 

public class UserDAO
{
public Map<String, Object> search(Map<String, Object> searchCriteriaMap)
{
String SEARCH_BY = (String)searchCriteriaMap.get("SEARCH_BY");
int start = (Integer)searchCriteriaMap.get("START");
int pageSize = (Integer)searchCriteriaMap.get("PAGESIZE");
if("ID".equals(SEARCH_BY))
{
int id = (Integer)searchCriteriaMap.get("ID");
//Get the total number of results for this criteria
int count = UserDAO.searchResultsCount(id);
// query the DB and get the start to start+pageSize results by applying filter on USER_ID column
List<User> users = search(id, start, pageSize);

}
else
{
String username = (String)searchCriteriaMap.get("USERNAME");
//Get the total number of results for this criteria
int count = UserDAO.searchResultsCount(username);
// query the DB and get the start to start+pageSize results by applying filter on USERNAME column
List<User> users = search(username, start, pageSize);

}
Map<String, Object> RESULTS_MAP = new HashMap<String, Object>();
RESULTS_MAP.put("DATA",users);
RESULTS_MAP.put("COUNT",count);
return RESULTS_MAP;
}

}

Наконец, код становится большим беспорядком и полностью нарушает объектно-ориентированные принципы. Есть много проблем с приведенным выше кодом.
1. Для каждого запроса на изменение сигнатуры методов меняются.
2. Код должен быть изменен для каждого расширения, такого как добавление большего количества критериев поиска.

Мы можем разработать лучшую объектную модель для этого вида функциональности поиска, которая является объектно-ориентированной и масштабируемой, следующим образом.

Универсальный критерий поиска, который содержит общие критерии поиска, такие как разбиение на страницы и детали сортировки.
 

package com.sivalabs.javabp;
public abstract class SearchCriteria
{
private boolean pagination = false;
private int pageSize = 25;
private String sortOrder = "ASC";

public boolean isPagination()
{
return pagination;
}
public void setPagination(boolean pagination)
{
this.pagination = pagination;
}
public String getSortOrder()
{
return sortOrder;
}
public void setSortOrder(String sortOrder)
{
this.sortOrder = sortOrder;
}
public int getPageSize()
{
return pageSize;
}
public void setPageSize(int pageSize)
{
this.pageSize = pageSize;
}

}

Универсальный объект SearchResults, который содержит фактические результаты и другие данные, такие как общее количество доступных результатов, поставщик результатов по страницам и т. Д.
 

package com.sivalabs.javabp;

import java.util.ArrayList;
import java.util.List;

public abstract class SearchResults<T>
{
private int totalResults = 0;
private int pageSize = 25;
private List<T> results = null;

public int getPageSize()
{
return pageSize;
}
public void setPageSize(int pageSize)
{
this.pageSize = pageSize;
}
public int getTotalResults()
{
return totalResults;
}
private void setTotalResults(int totalResults)
{
this.totalResults = totalResults;
}

public List<T> getResults()
{
return results;
}
public List<T> getResults(int page)
{
if(page <= 0 || page > this.getNumberOfPages())
{
throw new RuntimeException("Page number is zero or there are no that many page results.");
}
List<T> subList = new ArrayList<T>();
int start = (page -1)*this.getPageSize();
int end = start + this.getPageSize();
if(end > this.results.size())
{
end = this.results.size();
}
for (int i = start; i < end; i++)
{
subList.add(this.results.get(i));
}
return subList;
}

public int getNumberOfPages()
{
if(this.results == null || this.results.size() == 0)
{
return 0;
}
return (this.totalResults/this.pageSize)+(this.totalResults%this.pageSize > 0 ? 1: 0);
}
public void setResults(List<T> aRresults)
{
if(aRresults == null)
{
aRresults = new ArrayList<T>();
}
this.results = aRresults;
this.setTotalResults(this.results.size());
}

}

Класс SearchCriteria, специфичный для поиска пользователей.
 

package com.sivalabs.javabp;

public class UserSearchCriteria extends SearchCriteria
{
public enum UserSearchType
{
BY_ID, BY_NAME
};

private UserSearchType searchType = UserSearchType.BY_NAME;
private int id;
private String username;

public UserSearchType getSearchType()
{
return searchType;
}
public void setSearchType(UserSearchType searchType)
{
this.searchType = searchType;
}

public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
}

Класс SearchResults, специфичный для поиска пользователей.
 

package com.sivalabs.javabp;
import java.text.MessageFormat;

public class UserSearchResults<T> extends SearchResults<User>
{
public static String getDataGridMessage(int start, int end, int total)
{
return MessageFormat.format("Displaying {0} to {1} Users of {2}", start, end, total);
}

}

UserService принимает SearchCriteria, вызывает DAO и получает результаты, подготавливает UserSearchResults и возвращает его обратно.
 

package com.sivalabs.javabp;

import java.util.ArrayList;
import java.util.List;

import com.sivalabs.javabp.UserSearchCriteria.UserSearchType;
public class UserService
{
public SearchResults<User> search(UserSearchCriteria searchCriteria)
{
UserSearchType searchType = searchCriteria.getSearchType();
String sortOrder = searchCriteria.getSortOrder();
System.out.println(searchType+":"+sortOrder);
List<User> results = null;
if(searchType == UserSearchType.BY_NAME)
{
//Use hibernate Criteria API to get and sort results based on USERNAME field in sortOrder
results = userDAO.searchUsers(...);
}
else if(searchType == UserSearchType.BY_ID)
{
//Use hibernate Criteria API to get and sort results based on USER_ID field in sortOrder
results = userDAO.searchUsers(...);
}

UserSearchResults<User> searchResults = new UserSearchResults<User>();
searchResults.setPageSize(searchCriteria.getPageSize());
searchResults.setResults(results);
return searchResults;
}

}

 

package com.sivalabs.javabp;
import com.sivalabs.javabp.UserSearchCriteria.UserSearchType;

public class TestClient
{
public static void main(String[] args)
{
UserSearchCriteria criteria = new UserSearchCriteria();
criteria.setPageSize(3);
//criteria.setSearchType(UserSearchType.BY_ID);
//criteria.setId(2);

criteria.setSearchType(UserSearchType.BY_NAME);
criteria.setUsername("s");

UserService userService = new UserService();
SearchResults<User> searchResults = userService.search(criteria);

System.out.println(searchResults.getTotalResults());
System.out.println(searchResults.getResults().size()+":"+searchResults.getResults());
System.out.println(searchResults.getResults(1).size()+":"+searchResults.getResults(1));
}
}

При таком подходе, если мы хотим добавить новый критерий, например поиск по EMAIL, мы можем сделать это следующим образом:
1. Добавить тип критерия BY_EMAIL в перечисление UserSearchType
2. Добавить новое свойство «email» в UserSearchCriteria
3. crit.setSearchType (UserSearchType.BY_EMAIL );
   criteria.setEmail ( «Gmail»);
4. В UserService подготовьте HibernateCriteria с фильтром электронной почты.

Это оно ?

От: http://sivalabs.blogspot.com/2011/02/java-coding-best-practices-better.html