JPA datetime 오류
JPA에서는 persistence context 라는 개념이 있어서, 사실상 DB와 Application 사이에 하나의 proxy가 더 존재한다. 캐시나 관리 등의 측면에서 장점도 분명이 있지만, mybatis와 같이 direct로 데이터를 주고 받을 때와 다르게 data의 sync가 맞지 않으면 곤란한 경우가 발생한다. 그 중에 하나가 datetime… 더 보기 »JPA datetime 오류
JPA에서는 persistence context 라는 개념이 있어서, 사실상 DB와 Application 사이에 하나의 proxy가 더 존재한다. 캐시나 관리 등의 측면에서 장점도 분명이 있지만, mybatis와 같이 direct로 데이터를 주고 받을 때와 다르게 data의 sync가 맞지 않으면 곤란한 경우가 발생한다. 그 중에 하나가 datetime… 더 보기 »JPA datetime 오류
간혹 쿼리를 할 때, 한 번에 select하기 위해 in절을 이용하게 된다. 다만 이 in절이라는 게 순서를 보장하지 않는다. optimizer에 plan에 따라서 움직이겠지만, 결국은 해당 값이 있는지만 체크하기 때문에 실제로 in절 내의 순서는 내가 List로 전달했다 하더라도 쿼리 결과에 별 영향을… 더 보기 »Mysql In절의 ordering
1 2 3 4 5 6 7 8 9 10 |
<html> <p>echo "not evil"</p> <script> document.addEventListener('copy', function(e) { console.log(e); e.clipboardData.setData('text/plain', 'echo "evil"\r\n'); e.preventDefault(); }); </script> </html> |
페이스트 재킹이라고, 카피 이벤트를 핸들링해서 유저가 보고 있는 화면과, 복사된 화면이 다르게 하는 경우가 있다. 위의 경우에는 echo “not evil” 이 실행될 거라 예상하고 복사해서 터미널에 붙이는 순간, 실제로는 echo “evil”이 실행된다. 물론 대부분 관리자 권한에 대해서는 따로 체크하겠지만,… 더 보기 »페이스트재킹
Intellij 15부터는 각 테스트를 IDE Code창에서 바로 실행시키는 건 알고 계시겠죠? 왼쪽 편에 화살표를 누르면 바로 테스트 실행이 뜹니다. class도 마찬가지고, method도 마찬가집니다. 사실 단축키(ctrl + shift + R)로 해당 메소드에서 실행을 시키기도 합니다. 근데, 계속 테스트 컨텍스트를 선택하라는 메시지가… 더 보기 »intellij 설정 – junit을 쉽게 하는 방법
매번 mysql만 사용하고, oracle 쿼리를 만들어 본 적 없었는데, 약간은 뜬금없는 오류에 대해서 겪은 내용 공유합니다. 아래와 같은 쿼리가 있습니다. baseDate는 String값이고 예를 들면, 20140810 과 같은 값을 가지고 있습니다. 특정 날짜 이후의 데이터에 대해서 조회하는 로직입니다.
1 2 3 4 5 6 7 8 9 |
<select id="selectId" resultType="string"> SELECT DISTINCT(id) FROM test WHERE id = #{id} AND time <![CDATA[>]]> to_date(#{baseDate}) </select> |
개발DB에서 성공적으로… 더 보기 »oracle query troubleshooting
1 2 3 4 5 6 7 8 9 10 11 |
@RequestMapping(value = "/test", method = RequestMethod.PUT) @ResponseBody public Info find(@PathVariable String id) throws Exception { Info info = infoRepository.select(id); if (info == null) { throw new CustomException("NOT_EXIST", "info is not existed"); } return info; } |
단순히 Exception을 throw하면, ExceptionHandler가 Code와 Message를 json형태로 잘 매핑해서 응답하게 됩니다. (추가로 HttpStatusCode까지도 매핑하게 됩니다) 그래서 아래처럼 테스트를 하게 되면 오류가 발생하게 됩니다.
1 2 3 4 5 6 |
@Test public void findIfInfoIsNull() { givenNullIfSelect(); mockMvc.perform(put("/test")).andExpect(status().isBadRequest()); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Caused by: kr.co.freeim.test.exception.CustomException: info is not existed at kr.co.freeim.test.TestApiController.find(TestApiController.java:93) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:953) ... 38 more |
그렇다면 보통 아래처럼 테스트를 고치게 되겠죠?
1 2 3 4 5 6 |
@Test(expected = CustomException.java) public void findIfInfoIsNull() { givenNullIfSelect(); mockMvc.perform(put("/test")).andExpect(status().isBadRequest()); } |
그러고 나서 테스트를 돌리게 되면 또다른 오류가… 더 보기 »MockMvc에서 ExceptionHandler 등록해서 쓰는 방법
Spring Batch 코드를 만들 때, 보통 아래와 같은 코드를 이용해서 jobParameter를 전달받습니다.
1 2 |
String maxCountParams = chunkContext.getStepContext().getStepExecution() .getJobParameters().getString("maxCount"); |
물론 배치를 실행할 때, argument를 주게 되겠죠.
1 |
classpath:jobs/dormant/sendmail/batch-job.xml maxCount=1 |
그런데 말입니다… maxCount=1 숫자입니다. 그렇다면, 아래와 같은 코드도 가능하지 않을까요?
1 |
long maxCountParams = chunkContext.getStepContext().getStepExecution().getJobParameters().getLong("maxCount"); |
실제로 JobParameters는 getLong(), getString(), getDate(), getDouble() 메소드를 지원합니다.… 더 보기 »Spring Batch의 getJobParameters()의 동작법
// Wed Apr 29 00:00:00 KST 2015 FastDateFormat.getInstance(“yyyyMMdd”).parse(“20150429”); // throws ParseException FastDateFormat.getInstance(“yyyyMMdd”).parse(“2015”); // Thu Mar 16 00:00:00 KST 81724 FastDateFormat.getInstance(“yyyyMMdd”).parse(“20150429113100”); 위와 같은 내용이고요, DateFormat과 입력값의 형태가 다를 때 결과가 다르게 나옵니다. yyyyMMdd 포맷이고, 20150429 인 경우는 정상적으로 2015/4/29로 파싱되구요(시간은… 더 보기 »FastDateFormat(SimpleDateFormat) 오동작
URL을 생성할 때 QueryString을 조합하는 문제가 있었다. 적당하게 &와 ?를 버무려야하는데, 딱히 이쁜 생각이 들지 않을 때 아래처럼 apache commons나 spring component를 이용하면 된다. http://www.leveluplunch.com/java/examples/build-convert-map-to-query-string/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
@Test public void test() throws Exception { UriComponents components = UriComponentsBuilder.fromUriString("http://www.freeism.co.kr/test?id=1000").query("name=freeism").build(); System.out.println(components.toUriString()); UriComponents components2 = UriComponentsBuilder.fromUriString("http://www.freeism.co.kr/test").query("name=freeism").build(); System.out.println(components2.toUriString()); UriComponents components3 = UriComponentsBuilder.fromUriString("http://www.freeism.co.kr/test?id=1000").query( "name=freeism&email=freeism@freeism.co.kr").build(); System.out.println(components3.toUriString()); List<NameValuePair> list = Lists.newArrayList(); list.add(new BasicNameValuePair("name", "freeism")); URIBuilder builder = new URIBuilder("http://www.freeism.co.kr/test?id=1000").addParameters(list); System.out.println(builder.build()); URIBuilder builder2 = new URIBuilder("http://www.freeism.co.kr/test").addParameters(list); System.out.println(builder2.build()); list.add(new BasicNameValuePair("email", "freeism@freeism.co.kr")); URIBuilder builder3 = new URIBuilder("http://www.freeism.co.kr/test?id=1000").addParameters(list); System.out.println(builder3.build()); } |
java.lang.ArrayIndexOutOfBoundsException: 13 at sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(BaseCalendar.java:454) ~[na:1.7.0_45] at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2333) ~[na:1.7.0_45] at java.util.GregorianCalendar.computeFields(GregorianCalendar.java:2248) ~[na:1.7.0_45] at java.util.Calendar.setTimeInMillis(Calendar.java:1140) ~[na:1.7.0_45] at java.util.Calendar.setTime(Calendar.java:1106) ~[na:1.7.0_45] at java.text.SimpleDateFormat.format(SimpleDateFormat.java:955) ~[na:1.7.0_45] at java.text.SimpleDateFormat.format(SimpleDateFormat.java:948) ~[na:1.7.0_45] at java.text.DateFormat.format(DateFormat.java:296) ~[na:1.7.0_45] 위처럼 에러가 나고, 확인했더니 SimpleDateFormat은 thread-safe 하지 않다고 한다. 멀티 스레드 환경에서 값의 변조에… 더 보기 »SimpleDateFormat은 Multi-thread 환경에서 쓸 수 없다.