'All Category'에 해당되는 글 500건
- 2017.05.09 Builder 와 Factory
- 2017.05.08 @Bean 사용법
- 2017.05.08 mysql DateTime간단 insert
- 2017.04.27 Worker Thread 패턴
- 2017.04.25 각종 DB에러 exception
- 2017.04.20 Regex 5
- 2017.04.14 spring @Async @Retryable
- 2017.04.12 reflection 2
- 2017.04.07 workbench를 사용해서 MySQL ERD뽑기
- 2017.04.05 jQuery로 테이블내용 확인 및 비교 contains & filter
- 2017.04.03 RestTemplate Exception잡기
- 2017.03.24 spring 이전페이지 복귀 1
- 2017.03.16 MEAN, LAMP & Others
- 2017.03.16 bootstrap 모달팝업 & aria-hidden & text Color
- 2017.03.14 jQuery DatePicker TimePicker
- 2017.03.13 Jms + ActiveMQ
- 2017.03.09 Java LocalDateTime을 mySql에 저장하기
- 2017.03.09 몇몇 Annotation
- 2017.03.07 Mockito 2 - parameter Hooking
- 2017.03.02 MySQL 시간 비교
- 2017.03.02 배치 실행 @Scheduled
- 2017.02.20 mysql Enum
- 2017.02.19 MySql Row Lock
- 2017.02.15 andExpect에서 jsonPath Matchers 등 고급비교
- 2017.02.13 빌드시스템 browserify, module-js
- 2017.02.10 maven (mvn) 수동빌드 + mojo +npm
- 2017.02.06 RestOperations
- 2017.01.31 bootstrap 테이블디자인
- 2017.01.21 @Cacheable
- 2017.01.20 Form Field @Validated 와 @Valid필드.
@Autowired 를 사용할 경우에는 classpath내에 존재해야 하는데,
그게 여의치 않거나 새로운 생성자가 필요한 경우에는
생성자를 직접 정의하면서 @Bean을 사용할 수 있다.
1.정의시:
@Configuration
public class OkHttpClientConfig { | ||
@Bean(name = CHAT_BILLING_HTTP_CLIENT)
OkHttpClient chatBillingHttpClient() {
OkHttpClient client = new OkHttpClient();
client.setTimeOut(blabla)
return client;
}
2.사용시:
@Qualifier(OkHttpClientConfig.CHAT_BILLING_HTTP_CLIENT)
@Autowired
OkHttpClient chatBillingHttpClient;
mySql에 dateTime을 지정해서 입력하는 경우,
STR_TO_DATE('12-01-2014 00:00:00','%m-%d-%Y %H:%i:%s') 을 사용해서 insert할 수도 있지만
아래와 같이 간단히 숫자로도 가능하다.
insert into campaign(campaign_code,version,memo,start_at,end_at,created_at,updated_at) values('bogo',1,'test',20170509150000,20170511110000,now(),now());
내가 많이 쓰는 간단한 방식은
String pattern = "[Tt]he"; // [A-Za-z] \\s \\w \\d
boolean matched = "the".matches(pattern); //=> true : 시작^ 끝$까지 포함해서정확히 일치해야 함.
(참고: https://codechacha.com/ko/java-regex/)
알파벳/숫자/언더바만 포함되었는지 체크하려면..
String pattern = "^[\\w]*$";
boolean a = "abc123".matches(pattern);
알파벳/숫자 만 체크하려면..
String pattern = "^[a-zA-Z0-9]*$";
boolean a = "abc123".matches(pattern);
그룹에서 \1 즉 "\\1"은 첫번째 엘리먼트를 뜻함. = m.group(1)
\\b (바운더리) 는 이스케이프 캐릭터로서 단어를 찾을때 앞뒤에 넣으면 좋음.
중복된 단어 하나로 만들기 예제
// String regex = "\\b(\\w+)\\b(?=.*\\b\\1\\b)";
// String regex = "(\\b\\w+)(\\W\\1\\b)+";
//이것도 잘되나 String regex = "\\b(\\w+)(\\s\\1)+\\b";
String regex = "(\\w+)(\\s\\1)+"; //이게 제일 심플하면서 어느정도 동작 확인.
//스페이스 제외하고 앞부분만을 word로 보는듯.
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
String input = in.nextLine();
Matcher m = p.matcher(input);
// Check for subsequences of input that match the compiled pattern
while (m.find()) {
// System.out.println("==" + m.group() + "," + m.group(1)+".");
input = input.replaceAll(m.group() , m.group(1));
}
// Prints the modified sentence.
System.out.println(input);
Regex Test 사이트는 https://regex101.com/
java API: https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html
Regex자료는 여기서 참고: Ref
| 표현식 | 설명 |
| ^ | 문자열의 시작, [^a-z] 에서는 NOT의 의미로 쓰임. |
| $ | 문자열의 종료 |
| . | 임의의 한 문자 (문자의 종류 가리지 않음) 단, \ 는 넣을 수 없음 |
| * | 앞 문자가 없을 수도 무한정 많을 수도 있음 |
| + | 앞 문자가 하나 이상 |
| ? | 앞 문자가 없거나 하나있음 |
| [] | 문자의 집합이나 범위를 나타내며 두 문자 사이는 - 기호로 범위를 나타낸다. []내에서 ^가 선행하여 존재하면 not 을 나타낸다. |
| {} | 횟수 또는 범위를 나타낸다. => {1,3} 콤마를 써야 함. |
| () | 소괄호 안의 문자를 하나의 문자로 인식 |
| | | 패턴 안에서 or 연산을 수행할 때 사용 |
| \s | 공백 문자 |
| \S | 공백 문자가 아닌 나머지 문자 |
| \w | 알파벳이나 숫자 (언더바도 체크함) |
| \W | 알파벳이나 숫자를 제외한 문자 |
| \d | 숫자 [0-9]와 동일 |
| \D | 숫자를 제외한 모든 문자 |
| \ | 정규표현식 역슬래시(\)는 확장 문자 역슬래시 다음에 일반 문자가 오면 특수문자로 취급하고 역슬래시 다음에 특수문자가 오면 그 문자 자체를 의미 |
| (?i) | 앞 부분에 (?i) 라는 옵션을 넣어주면 대소문자를 구분하지 않음 |
▲ 출처 : http://lng1982.tistory.com/141
spring 에서 async기능구현 필요가 있다면 메쏘드에 @Async를 사용하면 된다.
이 때, Application에는 @EnableAsync가 필요하다.
Retryable은 몇번의 실패을 다시 해주게 해주는데, 아래와 같이 사용하면된다.
정해진 실패 회수를 초과할 경우 @Recover함수가 불리게 되는데, exception + 파라미터가 동일하다는 점에 주의!
Application에는 @EnableRetry가 필요하다.
private static final int MAX_ATTEMPTS = 4; private static final long BACK_OFF_DELAY = 2000; //msec
@Retryable(value = DataAccessResourceFailureException.class,
maxAttempts = MAX_ATTEMPTS,
backoff = @Backoff(delay = BACK_OFF_DELAY))
public void checkAndProcess(String userKey, String itemId) {@Recover
public void recover(DataAccessResourceFailureException e, String userKey, String itemId) {
log.error("All retries have failed!, userKey:{} " + e.toString(), userKey);
}
private 함수를 테스트 할 경우, java의 reflection기능을 사용해서 test할 수 있다.
import java.lang.reflect.Method;
Map<String, Long> repeatMap = null;
try {
Method method = ChatRankingService.class.getDeclaredMethod("getRepeatCount", List.class);
method.setAccessible(true);
repeatMap = (Map<String, Long>)method.invoke(chatRankingService, serviceCodes);
} catch (Exception e) {
}
이와 같이 method.setAccessible(true)를 하면 private함수도 실행이 가능하다.
oracle에서 workbench를 다운로드 해서( GPL 라이선스라 로그인은 필요함)
실행한 후,
Database메뉴에서
=> Reverse Engineering을 하면,
기존시스템의 ERD를 뽑을 수 있게 된다.
화면의 table 에 이미 있는 내용을 사용자가 또 추가입력하는 경우
(즉, 팝업을 통해서 새로운 데이타를 추가하는데, 화면에 이미 있는내용과 중복되는지 체크하는 경우)
서버를 통하지 않고, jQuery를 통해서 쉽게 방지해서 alert창을 뛰울 수 있다.
<script type="text/javascript"> function nameCheck() { var name = $('#myInput').val();
if ($('.listedName:contains(' + name + ')').length != 0) {
alert('already Exists');
return false;
}
return true;
} </script>
<table>
<tr>
<td>ID1</td>
<td class="listedName">name1</td> // name2, name3 등 반복</tr>
.....
<form onsubmit="'return nameCheck()'">
<input id="myInput"/>
그런데, contains함수가 문제가 약간있다.
Entity단위(테이블 TD단위)로 비교를 하지 않고... Entity안에 단어가 포함되는지를 비교하는 문제가 있다.
그래서 정확하게 비교를 하고 싶을때는 filter를 쓸 필요가 있다. REF
if ($('.listedName').filter(function() {
return $(this).text() === name;
}).length !=0 ) {
RestTemplte.getForEntity 등 각종 함수에서 RestClientException (RUNTIME Exception)을 throw한다.
하지만 이 에러는 code를 표시하지 않기 때문에,
HttpClientErrorException을 catch하는게 좋다.
Class HttpClientErrorException
이렇게 HttpClientErrorException은 RestClientException을 상속받으며, 우리가 http를 주로사용하기 때문에
대부분이 여기에 해당된다.
사용법은 REF 첫번째 answer과 같이... 사용하면 된다.
try {
ResponseEntity<StoreDto> r = restTemplate.getForEntity(url, StoreDto.class, m);
}
catch (final HttpClientErrorException e) {
System.out.println(e.getStatusCode());
System.out.println(e.getResponseBodyAsString());
}404에러 등이 잡힌다.
spring에서 이전페이지로 복귀는 request의 referer를 이용해서 가능하다.
이전 페이지에 추가적인 데이타를 보내고 싶다면
addFlashAttribute로 가능하다.
import javax.servlet.http.HttpServletRequest; import org.springframework.web.servlet.mvc.support.RedirectAttributes;
String myFunction(HttpServletRequest request,
RedirectAttributes redirectAttributes,
) {
redirectAttributes.addFlashAttribute("okList", "AA BB CC");
String referer = request.getHeader("Referer");
return "redirect:"+ referer;
}
LAMP stack: Linux, Apache, MySql, PHP (Perl/Python)
MEAN stack: MongoDB, ExpressJS, AngularJS, NodeJS
- ExpressJS: Node.js Web application framework.
- NodeJS: event-driven I/O server-side JavaScript Environment.
Play-framework : Scala & java 용 프레임워크. REF
- 이게 spring처럼 널리 이용된다면 scala도 그만큼 역량을 키워가겠네요.
- JSON과 xml을 First-class 고객으로 모시는 프레임워크인만큼 계속 영향력이 커지지 않을까요..
bootstrap을 이용해서 table도 쉽고 이쁘게 만들 수 있지만
팝업도 간단히 이쁘게 만들 수 있다.
게다가 팝업이 약간씩 멋지게 움직이는 효과도 있어 심미적으로 만족감도 상당하다.
팝업은 modal ( 팝업이 뜨면서 팝업안에서만 작업이 되는 팝업을 일컫는 단어) 을 이용하면 되고
팝업이 나타나면서 움직이는 효과는 aria-hidden을 이용하면 된다.. REF
bootstrap.min.css LINK 및
bootstrap.min.js 정도추가하면 되고.. 및
modal DIALOG의 구조는 아래와 같은 구조.
<div class="modal fade" role="dialog" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content"><div class="modal-header"><div class="modal-body"><div class="modal-footer">
TEXT Color
<p class="text-muted">...</p> //grey
<p class="text-primary">...</p> //light blue
<p class="text-success">...</p> //green
<p class="text-info">...</p> //blue
<p class="text-warning">...</p> //orangish,yellow
<p class="text-danger">...</p> //red웹상에서 간단히 날짜 선택창을 만들고 싶다면
jQuery의 DatePicker모듈을 이용하는 것이 좋아 보인다.
REF (우측 상단에 예제 있음)
TimePicker도 시간입력용으로 좋은데,
시간을 step단위로 지정이 가능하다. REF
var timeFix;
function timeFixClick(minutes) {
timeFix.val(minutes);
$('#timeModal').modal('show');
}
$(document).ready(function () {
timeFix = $('#timeFix');
datePicker.datepicker({
format: "yyyy-mm-dd",
viewMode: "days",
minViewMode: "days",
});
timePicker.timepicker();
timePicker.timepicker('option', 'step', '30'); //30분 단위로 시간 선택가능.
<html>
<input name="expireTime" class="form-control" data-time-format="H:i:s" id="timePicker" type="text" size="8" />
<a th:onclick="'javascript:timeFixClick(\'' + ${ticket.minutes} + '\');'" th:text="修正" />
<js 시간 Library> - REF: https://momentjs.com/
<script type="text/javascript" th:src="@{js/moment.min.js}"></script> 한 후에..
(사용 예)
var date = moment(dateData).format('YYYY-MM-DD');
var time = moment(dateDate).format('HH:mm:ss');
spring에서 msg를 전달하는 가장 흔한 방법은
JMS(+jmsTemplate)과 ActiveMQ(by apache)를 이용하는 방법이다. 물론 ASYNC 메시징이다.
ActiveMQ를 사용하고자 하면, ActiveMQConnectionFactory가 JMSConnectionFactory로써 제공된다.
-디폴트 포트: 61616
<Send>
ConnectionFactory cf = new ActiveMQConnectionFactory("tcp://localhost:61616";
Connection conn = cf.createConnection();
session = conn.createSession(..);
MessageProducer producer = session.createProducer(destination);
producer.send( session.createTextMessage('hello')..)
<Receive>
MessageConsumer consumer = session.createConsumer(destination);
Message message = consumer.receive();<JmsTemplate을 사용하면 좀더 간단해진다>
jmsOperations.send( new MessageCreator() { ......blabla }
또는 컨버팅 필요시
jmsOperations.convertAndSend
위의 REF를 참조해서 아래와 같이 string으로 변경하면
mySql에서 바로 사용이 가능하다.
Clock clock;
DateTimeFormatter DB_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String alarmTime = dateTime.format(DB_TIME_FORMAT);
String alarm2 = LocalDateTime.now(clock).format(DB_TIME_FORMAT)
그리고, LocalDateTime을 바로 MySQL에 저장도 가능한데.. (음 이게 더 나아보인다)
단, 이때 String 을 LocalDateTime으로 변환필요시가 있을때에는
"yyyy-MM-ddTHH:mm:ss") 형태를 사용해야 한다. ( T가 중요)
@ModelAttribute on a method argument - indicates the argument will be retrieved from the model. If not present in the model, the argument will be instantiated first and then added to the model.
@Retryable (a.Exception, b.Exception 지정가능. 미지정시 상시.. )
@Recover (a.Exception) : a.Exception으로 실패시에에만 호출되어서.. 특별한 뒷처리 및 로그작업등 가능.
@Component( name부여가능 )
@Profile("profile_name") - name에 해당하는 profile이 active일 때만, Bean이 생성됨. dev test등 주로 이용.
@Conditional - 조건부 Bean생성.
@GatherJob (USER_Defined)
- 나중에 모으고 싶은 것들을 임의로 선언한다. (class는 있어야 죠 , 간단한 빈깡통 Interface면 됨.)
Set<Class<?>> gather = New Reflections(My.class.getPackage().getName()) .getTypesAnnotatedWith(GatherJob.class); @Qualifier("my name") - |
Mokito의 ArgumentCaptor를 사용해, insert/update같은 동작을 간단하게 hooking할 수 있다.
(예제)
final ArgumentCaptor<Ticket> logCaptor = ArgumentCaptor.forClass(Ticket.class);
verify(chatTicketRepository).update(logCaptor.capture());
Ticket ticket = logCaptor.getValue();
assertThat(ticket.getStatus(), is(TicketStatus.EXPIRED));
좀 더 복잡한 경우는, ArgumentMatcher를 이용해 복잡한 비교를 할 수 있다.
파라미터를 hooking하는 다른방법: REF
when(mockObject.myMethod(any(parameterClass.class))).thenAnswer(
invocation -> invocation.getArgumentAt(0, parameterClass.class));시간비교시에는 MySQL의 INTERVAL 을 이용하는 것이 가장 편한 것으로 보인다.
예)
WHERE
myTable.datetimefield > now() - INTERVAL 3 DAY (or MONTH or WEEK )
==> 그러나, 날짜간에는 빼기 보다는 DATE_SUB를 사용해야 하고.
BETWEEN도 사용이 가능하지만,.. 작은 날짜를 앞에 써야 한다.
WHERE my_date BETWEEN DATE_SUB(now(), INTERVAL 1 WEEK) AND now()
참) 그리고 NULL을 비교할때는, IS NULL 또는 IS NOT NULL 을 사용해야 한다.
그 외 Doma라는 ORM에서 IF와 같이 쓰는 경우엔 다음과 같이 섞어서 사용할 수도 있다.
service.service_code
/*%if sortKey == @com.linecorp.fortune.constant.ServiceSortKey@NEW */
, IF ( NOW() - INTERVAL 1 WEEK < forum_service.published_at, 1, 0) as is_new
/*%elseif sortKey == @com.linecorp.fortune.constant.ServiceSortKey@ANSWER_TIME */
, IF (forum_service_condition.average_answer_time = 0, 12, forum_service_condition.average_answer_time) as answer_time
/*%end*/
스프링으로 주기적으로 도는 함수를 만들고 싶으면...
아주 간단하게
public 함수위에
@Scheduled(cron = "0 39 */2 * * *")와 같이 선언하면 된다.
아래는 매일 2시 49분에 도는 함수!
@Scheduled(cron = "0 49 2 * * *")
참고: 제일앞자리는 cron에 없는거네요.. 용도는? 초. */30 이러면 매 30초마다임. REF
초 0-59 , - * /
분 0-59 , - * /
시 0-23 , - * /
일 1-31 , - * ? / L W
월 1-12 or JAN-DEC , - * /
요일 1-7 or SUN-SAT , - * ? / L #
년(옵션) 1970-2099 , - * /
* : 모든 값
? : 특정 값 없음
- : 범위 지정에 사용
, : 여러 값 지정 구분에 사용
/ : 초기값과 증가치 설정에 사용
L : 지정할 수 있는 범위의 마지막 값
W : 월~금요일 또는 가장 가까운 월/금요일
# : 몇 번째 무슨 요일 2#1 => 첫 번째 월요일
<cron 은 REF 참조>
crontab 파일 형식
------ -------- ---------------------------------------------------
필 드 의 미 범 위
------ -------- ---------------------------------------------------
첫번째 분 0-59
두번째 시 0-23
세번째 일 0-31
네번째 월 1-12
다섯번째 요일 0-7 (0 또는 7=일요일, 1=월, 2=화,...)
여섯번째 명령어 실행할 명령을 한줄로 쓴다.
------ -------- ---------------------------------------------------
예) 5 */2 * * * 명령어 => 매일 2시간간격으로 5분대에
InnoDB Lock: REF
엔진확인
mysql> show engines;
mysql> show variables; (autocommit등의 Variables 확인)
Spring 의 @Transactional과 함께 사용하면 autcommit이 일반적으로는 자동으로 풀렸다가 다시 설정 된다.. REF(Stack-overFlow) REF
=======================================
Mysql에서 엔진이 innodb(요즘 보통다 이러함)일 경우, row level lock이 지원된다.REF
- start transaction -> 작업 -> commit 순으로 수행하면 된다.
start transaction;
select * from sales_item where id=99 lock in share mode;
commit;
transaction도중에 다른 작업이 또 들어오면
1. wait이 되거나 (위 REF 참조)
2. 자동으로 에러가 나서 거절이 되는데..
(1,2번 중 원하는 데로 선택을 어떻게 하는지 ... 연구 중)
select for update도 있긴한데.. 한 문장 밖에 안되므로 좀 활용도가 떨어지지만
spring의 @Transactional과 같이 사용하기에 유리하다. REF
일단 설정 파일에서
init_connect = 'set autocommit=0'
으로 설정하여 오토커밋을 해제합니다. 기본적으로 mySQL 설치 시엔 오토커밋일 설정되어 있습니다
innodb_lock_wait_timeout 확인 방법.
show variables like '%wait_timeout%';
MockMvc결과가 문자열 json일 때 쉽게 체크하는 법은 jsonPath를 사용하는 방법. REF
그러나, 결과가 Attribute내에 Object 등으로 오는 경우가 많은데..
이럴 경우에는 org.hamcrest.Matchers 를 사용하면 좋다.
<Attribute가 단순 String일 경우>
.andExpect(model().attribute("ReserveResult", org.hamcrest.Matchers.containsString("rtncd=0000")));
<(result내의 Model) Attribute가 Json Object일 경우> org.hamcrest.Matchers.CustomMatcher를 사용한다.
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
//Check if rtncd="0000"
Matcher< ResultClass > resultMatcher = new CustomMatcher< ResultClass >("") {
public boolean matches(Object object) {
return ((object instanceof ResultClass) && ((ResultClass) object).getRtncd().equals("00"));
}
};
mockMvc.perform(request)
.andDo(print())
.andExpect(status().is3xxRedirection())
.andExpect(model().attribute("reserveResult", resultMatcher));
========node와 연계해서 빌드도 가능===========REF
spring과 npm을 연계해서 빌드시에,
$npm run build:js 등으로 package.json을 이용해 직접 build명령을 내릴 수도 있다.
npm REF
- npm은 필요모듈 설치시, 글로벌설치/로컬설치 가능. 일반적으로는 로컬설치가 좋지만 선택이 어려울경우가 있다.
npm 환경설정 npm
각종 npm 패키지 명령: REF
- npm list browserify
- npm view browerify version ( 최신버전확인)
- npm remove browserify
- npm install (-g) browserify (-g: global)
<빌드 LifeCycle>: REF
default 빌드 LifeCycle은 다음과 같다.
- validate - validate the project is correct and all necessary information is available
- compile - compile the source code of the project
- test - test the compiled source code using a suitable unit testing framework. These tests should not require the code be packaged or deployed
- package - take the compiled code and package it in its distributable format, such as a JAR.
- verify - run any checks on results of integration tests to ensure quality criteria are met
- install - install the package into the local repository, for use as a dependency in other projects locally
- deploy - done in the build environment, copies the final package to the remote repository for sharing with other developers and projects.
아래와 같이 springBoot 실행도 가능.
$mvn springboot run REF
------------------------------------------------------------------------------------------------------------
=====maven 확인======== REF
$mvn -version
없으면 mac의 경우에는 brew install maven
=======BUILD+package=============== REF
$mvn package
빌드 에러메시지를 상세히 보고 싶다면
$mvn -e -X package
===기본설명===
spring 에서 pom.xmL을 이용해 maven 의존성 관리를 한다.
maven을 직접사용하는 방법도 있는데
maven 설치 후
$ mvn archetype:generate 해서 각종 값을 넣고 나면 (주로 package 명으로 넣으면된다)
pom.xml까지 자동 생성이 되며,
$ mvn compile exec:java -Dexec.mainClass=com.ky.App
으로 바로 커맨드 상에서 실행한다.
매우 유용하다.
=== MOJO ============
MOJO : Maven plain Old Java Object 이다. REF
- maven의 java-plugin을 만드는데 사용된다.
만들때는 @Mojo를 써서 간단히 만들 수 있다는데, 만들일 은 잘 없고.. 쓸 일은 많으니.. .. 쓸 때 위주로 보면.
| groupId | This is the group ID for the plugin, and should match the common prefix to the packages used by the mojos |
| artifactId | This is the name of the plugin |
| version | This is the version of the plugin |
| packaging | This should be set to "maven-plugin" |
| dependencies | A dependency must be declared to the Maven Plugin Tools API to resolve "AbstractMojo" and related classes |
plugin내의 mojo를 실행시키려면
- "$mvn sample.plugin:hello-maven-plugin:1.0-SNAPSHOT:sayhi" 로도 가능하다.
($mvn groupId:artifactId:version:goal )
spring 컨트롤러나 서비스 등의 안에서, 웹 서비스를 접속할 필요가 있을경우 RestTemplate(혹은 RestOperation)을 이용하면 편리하다.
get/post
For
Object/Entity 의 형태로 함수명이 존재한다.
즉 결과를 Object형태로 받을 수도 있고, Entity형태로 받을 수도 있다.
파라미터로
-Object, Map, ClassType 등을 다양하게 이용할 수 있다.
patch/update/delete/option 함수들도 존재한다.
spring에서
@Cacheable
@Cacheevict
@Cacheput
을 이용해서 각종 조건을 지정하면서... 함수 리턴결과 등을 cache할 수 있다.
단, Cacheable등은 proxy형태의 AOP이므로, 별도 class를 만들어서 추가해야 한다.
@CacheConfig(cacheNames = CacheManagerConfig.DEFAULT_CACHE_NAME) public class ForCacheService {
@Cacheable(key = "'myCache'")
public Optional<Something> findSomething(){
return db.findSomething();
}
@CacheEvict(key = "'myCache'")
public void clearCache() {
log.info("clear cache: key=myCache");
}}
==> 음 Optional은 serialize가 안되서 Cache가 안되는 문제 발견.
일반 class 일 경우에도 implements Serializable을 해야 한다.
null Value가 cache가 안되도록 하려면 다음과 같이 #unless를 이용하면 된다. REF
(unless는 함수가 실행된 이후에 평가됨. 즉, cache가 있을때는 상관없이 pass.)
@Cacheable( key="'myCache'", unless="#result == null")
public Person findPerson(int pk) {
return getSession.getPerson(pk);
}null Value도 cache가 되도록 하고 싶으면? ==>안됨.
REF-spring REF-etc Validataion-Group
spring 에는 annotation으로 form 필드를 자동으로 체크할 수가 있다.
일반적으로는
xxForm.java 파일을 하나 만들고 그 안에 Form과 일치하도록 모든 Field를 정의한 다음에...
각 필드별로 여러가지 constraint를 정의할 수 있다.
@Pattern : Regex패턴 체크.
@NotNull @Null
@Size(min=2, max=30)
@Past (지난 날짜) @Future
@Max @Min @DecimalMax @DecimalMin 2Digits
@AssertFalse @AssertTrue
<기본사용>
예제)
public class MyForm {
@NotNull(message = "{error.not_input}")
@Length(max = 3, message = "{error.max_length}")
private String age;
@NotNull(message = "{error.not_input}")
@Length(max = 255, message = "{error.max_length}")
private String name;
@Pattern(regexp = MyConstant.MAIL_ADDRESS, message = "{error.mail_address_format_error}")private String email;
}
컨트롤러에서 아래와 같은 형태로 값을 받는다.
public String checkPersonInfo(@Valid PersonForm personForm, BindingResult bindingResult)
==========1=====================
간단히 Override로 Custom Validator를 만들 수 있다.
@Data
@NoArgsConstructor
@CmsMemberMailValidator(newMailAddressField = "newMailAddress", mailConfirmField = "mailConfirm")
public class CmsMemberMailModifyForm {
private Long id;
private String displayName;
private String status;
private String organizationName;
private String mailAddress;
@NotNull(message = "{error.mail_address_null_error}")
@Pattern(regexp = WebConstantShared.MAIL_ADDRESS, message = "{error.mail_address_format_error}")
private String newMailAddress;
@NotNull(message = "{error.mail_address_null_error}")
private String mailConfirm;
public static CmsMemberMailModifyForm of(CmsMemberDetail cmsMemberDetail) {
CmsMemberMailModifyForm form = new CmsMemberMailModifyForm();
BeanUtils.copyProperties(cmsMemberDetail, form);
return form;
}
}
| @Target({ TYPE }) @Documented | |
| @Retention(RUNTIME) | |
| @Constraint(validatedBy = Validator.class) | |
| public @interface CmsMemberMailValidator { | |
| String message() default "{error.mail_address_format_error}"; | |
| Class<?>[] groups() default {}; | |
| Class<? extends Payload>[] payload() default {}; | |
| String newMailAddressField(); | |
| String mailConfirmField(); | |
| class Validator implements ConstraintValidator<CmsMemberMailValidator, Object> { | |
| private String message; | |
| private String newMailAddressField; | |
| private String mailConfirmField; | |
| @Override | |
| public void initialize(CmsMemberMailValidator constraintAnnotation) { | |
| message = constraintAnnotation.message(); | |
| newMailAddressField = constraintAnnotation.newMailAddressField(); | |
| mailConfirmField = constraintAnnotation.mailConfirmField(); | |
| } | |
| @Override | |
| public boolean isValid(Object value, ConstraintValidatorContext context) { | |
| BeanWrapper beanWrapper = new BeanWrapperImpl(value); | |
| String newMailAddressValue = (String) beanWrapper.getPropertyValue(newMailAddressField); | |
| String mailConfirmValue = (String) beanWrapper.getPropertyValue(mailConfirmField); | |
| //check only different case (null & blank Error_Message is processed on @NotNull annotation) | |
| if (newMailAddressValue != null && StringUtils.isNotBlank(newMailAddressValue) | |
| && mailConfirmValue != null && StringUtils.isNotBlank(mailConfirmValue) && !mailConfirmValue | |
| .equals(newMailAddressValue)) { | |
| context.disableDefaultConstraintViolation(); | |
| context.buildConstraintViolationWithTemplate(message) | |
| .addPropertyNode(mailConfirmField) | |
| .addConstraintViolation(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| } | |
| } |
==========2===================
위보단 약간 복잡하지만 좀 더 많은 기능이 있는 @InitBinder를 이용해
Custom Validator를 만들 수 있다.
Custom Validator: spring-REF Ref-InitBinder
Custom Validation Class를 만들 수도 있다.
.
< Valid vs Validated > REF1simple, REF2,
Validated는 spring 소속이고, Valid는 java소속인데
Validated가 Validataion-Group을 지정할 수 있다.
에러메시지도 출력가능하다. REF



