본격 정산모듈작성4
정산 페이지의 페이징과 검색을 마지막으로 작업하였다. (앞으로 더 하겠지만)
어제 대략적으로 정리된 코드들을 다시 봤는데 처참할 정도로 더러웠다.
다시 작성 중에 어제까지 안되었던 /list컨트롤러만으로 search까지 구현하기가 오늘 갑자기(?) 되어버렸다.
그나마 깔끔해진거 같은 코드 다행인듯..
검색 dto
@Getter
@RequiredArgsConstructor
@ToString
public class SettlementListSearchDto{
private final String searchOpt;
private final String searchWord;
private final String fromDate;
private final String toDate;
private final SettleStatus status;
}
컨트롤러
@RequestMapping({ "/list" })
public ModelAndView listSettlement(SettlementListSearchDto search,
@RequestParam(required=false,defaultValue="1") int nowPage,
@RequestParam(required=false,defaultValue="10") int cntPerPage) {
ModelAndView mav = new ModelAndView("settlement/list.tiles");
try {
int total = settlementService.selectAllCount(search);
PagingDto paging = PagingDto.builder()
.nowPage(nowPage)
.cntPerPage(cntPerPage)
.total(total)
.build();
List<EnumValue> settleStatus = ValueUtils.getMapper().getAll().get("SettleStatus");
List<SettlementListResponseDto> resList = settlementService.selectAllSettlement(search,paging);
mav.addObject("paging", CommonUtils.convertObjectToMap(paging));
mav.addObject("search", CommonUtils.convertObjectToMap(search));
mav.addObject("settleStatus", settleStatus);
mav.addObject("result", resList);
} catch (Exception e) {
this.logger.error("ERROR - ", e);
}
return mav;
}
검색 dto를 받아서 null 이면 검색 조건에 포함x 값이 있으면 필터링 하는 구조로 바꿔봤다.
어제해놨던 오버로딩은 더 이상 필요없어서 지워버림!
서비스
@Transactional(readOnly = true)
public List<SettlementListResponseDto> selectAllSettlement(SettlementListSearchDto search,PagingDto paging) throws Exception{
return settlementMapper.selectAllSettlement(search,paging)
.stream()
.map(SettlementListResponseDto::new)
.collect(Collectors.toList());
}
@Transactional(readOnly = true)
public int selectAllCount(SettlementListSearchDto search) throws Exception{
return settlementMapper.selectAllCount(search);
}
dao의 selectAllCount @param 어노테이션을 사용, count 쿼리와 list 쿼리가 동일한 where조건문을 쓰도록 하였다.
(이전에는 필드의 depth가 달랐다..)
@Repository
public interface SettlementMapper {
List<Reservation> selectAllSettlement(@Param("search")SettlementListSearchDto search,@Param("paging")PagingDto paging) throws Exception;
int selectAllCount(@Param("search")SettlementListSearchDto search) throws Exception;
int updateSettlementStatus(List<Reservation> domainList) throws Exception;
}
mybatis 부분에서 좀 공들인 부분이 생겼다.
java의 메소드를 콜하는 부분이 있던 것.
search dto의 null 혹은 공백 체크하기에 안성맞춤이었다.
참고링크 https://hayoung950518.tistory.com/43
https://cofs.tistory.com/309
공백 체크 메소드 작성
/**
* Object type 변수가 비어있는지 체크
*
* @param obj
* @return Boolean : true / false
*/
public static Boolean empty(Object obj) {
if (obj instanceof String) return obj == null || "".equals(obj.toString().trim());
else if (obj instanceof List) return obj == null || ((List<?>) obj).isEmpty();
else if (obj instanceof Map) return obj == null || ((Map<?, ?>) obj).isEmpty();
else if (obj instanceof Object[]) return obj == null || Array.getLength(obj) == 0;
else return obj == null;
}
/**
* Object type 변수가 비어있지 않은지 체크
*
* @param obj
* @return Boolean : true / false
*/
public static Boolean notEmpty(Object obj) {
return !empty(obj);
}
mybatis 와 매핑
mybatis.config를 통해 alias를 사용했는데 죽어도 안됐는데 이 방법을 찾아서 다행이다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="Function">
<sql id="checkEmpty">
<bind name="isEmpty" value=":[@com.guivingAdmin.utils.CommonUtils@empty(#this)]"/>
<bind name="isNotEmpty" value=":[@com.guivingAdmin.utils.CommonUtils@notEmpty(#this)]"/>
</sql>
</mapper>
실제 사용
<sql id="searchWhere">
<include refid="Function.checkEmpty"/>
<if test="#fn=isNotEmpty, #fn(search.status)">
...
</if>
(...)
</sql>
<select id="selectAllSettlement" resultMap="ResultMap.reservationResult">
SELECT
...
FROM
...
WHERE ...
<include refid="searchWhere"></include>
</select>
#fn= 이부분마저 줄이고 싶었으나 일단 만족하는걸로 !
또한가지.. 이전에 resultMap 추가할 때 interface를 만들었는데 이번 작업을 하면서 불필요하단 것을 깨닫게 되었다.
namespace로 매핑이 되는 것이었구나..
jpa를 먼저 본다지만 mybatis구조도 좀 공부해보는걸로
마지막으로 javascript 부분도 좀 바꿨다.
위의 controller 단에 바뀌어 다음과 같은 플로우로 서버와 통신하게 된다.
-
최초 페이지 접근 시 서버에서 검색 dto를 내려준다.
-
클라이언트는 검색 dto를 form안에 params 라는 클래스 태그를 입력한다.
-
검색을 진행할 시 각 params 클래스 인자들의 id를 토대로 class태그를 탐색한다.
-
탐색된 class의 값을 다시 params의 매치되는 요소에 부여한다.(여전히 params는 form안에 있다.)
-
서버에 form 데이터를 전송하여 결과를 받은 뒤 다시 params의 값을 해당 요소들에 부여한다.
(검색 조건 UI 적용)
말은 길어졌지만 코드 보면 간단.
var elements={
init : function(){
var _this = this;
$('#btn_search').click(function(event) {
_this.loadpage();
});
$('#btn_register').click(function(event) {
event.preventDefault();
location.href = $("#register_url").val();
});
$('.selectAll').click(function(event) {
var flag = $('.selectAll').is(':checked');
$('.select').each(function(){
$('.select').prop('checked',flag);
});
});
$('.countryIdx').click(function(){
_this.setcity();
$('.cityIdx').val("");
});
$('.cityIdx').click(function(){
_this.setcompany();
$('.companyIdx').val("");
});
$('.btn_update').click(function(){
_this.update($(this).data('status'));
});
$('.page').click(function(){
_this.gopage($(this).data('no'));
});
_this.getparams();
},
setcity : function (){
var tempCountryIdx = $('.countryIdx').val();
$('.cityIdx option').each(function(){
if ($(this).data("countryidx") == tempCountryIdx) {
$(this).show();
}
else{
$(this).hide();
}
});
},
setcompany : function(){
var tempCityIdx = $('#cityIdx').val();
$('.companyIdx option').each(function(){
if ($(this).data("cityidx") == tempCityIdx) {
$(this).show();
}
else{
$(this).hide();
}
});
},
getparams : function(){
var _this = this;
$(".params").each(function(){
var elm = '.'+$(this).attr('id');
var val = $(this).val()
if(val){
if($(elm).attr('type') == 'radio'){
$('input:radio'+elm+':input[value=' + val + ']').attr("checked", true);
}
else{
$(elm).val(val);
}
}
});
_this.setcity();
_this.setcompany();
},
setparams : function(){
$(".params").each(function(){
var elm = '.'+$(this).attr('id');
var val;
if($(elm).attr('type') == 'radio'){
val = $("input:radio"+elm+":checked").val();
}
else{
val = $(elm).val();
}
$(this).val(val);
});
},
loadpage : function(){
var _this = this;
_this.setparams();
$("#searchForm").submit();
},
gopage : function(pageNo){
$("#nowPage").val(pageNo);
var _this = this;
_this.loadpage();
}
}
elements.init();
코딩 방식은 배민의 갓동욱님이 쓰신 책을 보고 따라함..
이 방식으로 기존 js도 좀 바꿔놨다.
내일부턴 기존 페이지들을 정산페이지와 같은 방식으로 페이징 및 검색 필터 적용이 필요하겠다.
인생 !!