'Java'에 해당되는 글 18건
- 2016.04.07 자바에서 jpeg 이미지(사진) 사이즈 조절하기
- 2015.11.25 대용량 엑셀다운로드 참고
- 2015.11.03 JAVA에서 10만건 엑셀을 읽어서 DB에 넣어주고 싶습니다.
- 2013.08.20 한글깨질때!!!
- 2012.11.08 모바일 참고
- 2011.10.12 jad로 역컴파일..
- 2010.08.30 Design Pattern 정리 (헤드퍼스트 디자인 패턴)
- 2010.08.30 javascript singleton (자바스크립트 싱글턴)
- 2010.08.30 네이버 OpenAPI 를 AJAX 방식으로 쓰기위한 JSP 프록시 만들기
- 2010.06.22 [JAVA] iBATIS + OSCACHE 사용 시 Cache Model에 대한 이해
- 2010.05.17 java velocity 참조..
- 2010.01.19 자바 원하는 오라클 함수를..(Java Stored Procedures or Functions)
- 2009.12.15 ResourceBundle 사용하기
- 2009.03.30 ROME을 활용해서 Feed 생성하기
- 2009.03.29 Struts2 에서 확장자 제거하기
출처 : http://sexy.pe.kr/tc/642
[JAVA] 자바에서 jpeg 이미지(사진) 사이즈 조절하기
스윙을 공부하다가 이미지 다루는 일이 자주 생겨 공부할 겸 포스팅 하게 되네요~
1. 파일에서 이미지 불러오기
Image orginalImage = ImageIO.read(new File("사진파일명.jpg"));
2. 이미지 사이즈 수정
Image resizeImage = originalImage.getScaledInstance( 넓이, 높이, Image.SCALE_SMOOTH); //속도보다 이미지 부드러움 우선
(SCALE_AREA_AVERAGING, SCALE_DEFAULT, SCALE_FAST, SCALE_REPLICATE, SCALE_SMOOTH 중에 선택)
3. 결과물을 옮길 이미지 생성
BufferedImage newImage = new BufferedImage( 넓이, 높이, BufferedImage.TYPE_INT_RGB );
4. 생성한 이미지에 크기 수정된 이미지 그리기
Graphics g = newImage.getGraphics();
g.drawImage(resizeImage, 0, 0, this);
g.dispose();
5. 새로 생성한 이미지를 파일로 저장하기
ImageIO.write(newImage, "jpg", new File("새 사진파일명.jpg"));
#사용된 패키지
java.awt.Image, java.awt.image.BufferedImage, javax.imageio.ImageIO, java.io.File, java.awt.Graphics
| ||||||
| ||
시그시끄 님의 최근 게시물입니다. | |
2015-11-03 08:30:30 | |
2015-11-01 19:50:03 | |
2015-10-25 15:17:50 | |
2015-10-17 17:20:59 | |
2015-10-13 22:29:36 | |
2015-10-13 17:45:31 | |
2015-10-08 09:04:22 | |
2015-09-23 19:00:47 | |
2015-09-19 01:28:59 | |
2015-09-17 13:23:40 |
추천 elf2lf 데이터가 어떤지 잘 모르겠지만
힙 에러라 데이터가 안되면 가상 메모리 사용하면 될텐데 직접 컨트롤이 안되는 자바의 문제인것 같네요 C로 하면 메모리 지정해서 확인할 수 있을텐데 C를 이용해서 해보시고 가능하면 C로 DLL을 만들어서 사용하면 어떨까요 데이터가 어떤지 잘 모르겠지만 힙 에러라 데이터가 안되면 가상 메모리 사용하면 될텐데 직접 컨트롤이 안되는 자바의 문제인것 같네요 C로 하면 메모리 지정해서 확인할 수 있을텐데 C를 이용해서 해보시고 가능하면 C로 DLL을 만들어서 사용하면 어떨까요 |
2014-11-19 11:04:24 [덧글] |
추천 아래쪽에 내려가다보면 해당 내용이 있는 것 같네요 http://www.coderanch.com/t/424181/open-source/Read-xls-xlsx-file-format |
2014-11-19 11:20:43 [덧글] |
추천 LECHUCK 아마도 POI에서 엑셀의 모든 데이터를 메모리에 올린 다음 작업을 하기 때문에 그런거 같은데요.
먼저 이클립스 메모리가 아니라 애플리케이션 실행시 JVM에 넘겨줘야하는 -Xmx 옵션을 사용해서 메모리를 늘려줘 보시구요. 그래도 메모리 부족이 뜬다면 모두 메모리에 올리기에는 전체 데이터가 너무 크기 때문에 csv 파일로 해서 블럭단위로 읽으면서 DB에 추가하면 될 거 같습니다. 아니면 xml로 한다면 DOM 대신 SaxParser를 사용해야겠죠. 아마도 POI에서 엑셀의 모든 데이터를 메모리에 올린 다음 작업을 하기 때문에 그런거 같은데요. 먼저 이클립스 메모리가 아니라 애플리케이션 실행시 JVM에 넘겨줘야하는 -Xmx 옵션을 사용해서 메모리를 늘려줘 보시구요. 그래도 메모리 부족이 뜬다면 모두 메모리에 올리기에는 전체 데이터가 너무 크기 때문에 csv 파일로 해서 블럭단위로 읽으면서 DB에 추가하면 될 거 같습니다. 아니면 xml로 한다면 DOM 대신 SaxParser를 사용해야겠죠. |
2014-11-19 11:25:17 [덧글] |
추천 토루토루 윗분 말씀대로 이클립스 메모리 말고 run 실행 설정에서 -Xmx 옵션으로 메모리 설정하시면 될 듯 합니다.
그리고 단순 DB Insert 라면 건별로 업데이트 하지 마시고 addBatch 이용해서 최소 천건단위로 insert 하세요. 10만건을 정해진 시간(예 : 5초 이내) 안에 처리해야 되는게 아니라면 위 내용만으로도 작업에는 큰 문제 없어 보입니다. 윗분 말씀대로 이클립스 메모리 말고 run 실행 설정에서 -Xmx 옵션으로 메모리 설정하시면 될 듯 합니다. 그리고 단순 DB Insert 라면 건별로 업데이트 하지 마시고 addBatch 이용해서 최소 천건단위로 insert 하세요. 10만건을 정해진 시간(예 : 5초 이내) 안에 처리해야 되는게 아니라면 위 내용만으로도 작업에는 큰 문제 없어 보입니다. |
2014-11-19 11:54:40 [덧글] |
추천 만약 10만건을 5초 이내에 올려야 되는 상황이 되면 다른 방법을 써야 하나요?
댓글 읽고 갑자기 궁금해지네요 만약 10만건을 5초 이내에 올려야 되는 상황이 되면 다른 방법을 써야 하나요? 댓글 읽고 갑자기 궁금해지네요 |
2014-11-19 12:09:08 [덧글] |
추천 1 귀린. 대용량 업데이트를 이용해야죠.. 검색해보니 나오네요.참고해보세요.
http://fruitdev.tistory.com/111 대용량 업데이트를 이용해야죠.. 검색해보니 나오네요.참고해보세요. http://fruitdev.tistory.com/111 |
2014-11-19 14:35:59 |
추천 토루토루 사실 addBatch를 이용하여 대용량 처리를 하더라도 특정 시간안에 모든 작업을 처리해야 되는경우에는
싱글 스레드로 실행시 어느정도 한계가 있습니다. (제 생각) 저의 경우 대용량 처리를 할때에는 멀티스레드로 데이터의 각 범위를 정해서 addBatch를 이용하였습니다. 사실 addBatch를 이용하여 대용량 처리를 하더라도 특정 시간안에 모든 작업을 처리해야 되는경우에는 싱글 스레드로 실행시 어느정도 한계가 있습니다. (제 생각) 저의 경우 대용량 처리를 할때에는 멀티스레드로 데이터의 각 범위를 정해서 addBatch를 이용하였습니다. |
2014-11-19 18:19:01 |
추천 익명할래요 2년전인가.. 신입때
파일로된 190만 라인, 100~150MB 짜리의 텍스트 파일을 DB 인서트 한적있었는데. 저도 같은 증상 있었습니다. 파싱 완료 -> 인서트 하는 과정에서 파싱하다보니 변수에 데이터를 담아야 하는데 점점 많아 지면서 힙스페이스 오류가 나는 상황이었는데요. 1000건씩 파싱 -> 인서트 반복해서 해결했네요. saxparser도 찾아보니 라인별 파싱이라고 하는데요. 같은 방식으로 진행하면 되지 않을까 합니다. 2년전인가.. 신입때 파일로된 190만 라인, 100~150MB 짜리의 텍스트 파일을 DB 인서트 한적있었는데. 저도 같은 증상 있었습니다. 파싱 완료 -> 인서트 하는 과정에서 파싱하다보니 변수에 데이터를 담아야 하는데 점점 많아 지면서 힙스페이스 오류가 나는 상황이었는데요. 1000건씩 파싱 -> 인서트 반복해서 해결했네요. saxparser도 찾아보니 라인별 파싱이라고 하는데요. 같은 방식으로 진행하면 되지 않을까 합니다. |
2014-11-19 13:34:17 [덧글] |
추천 푸우날쎄 이전 프로젝트 중에 하루 교통카드데이터(서울기준, 일 하루 약 이천만건)를
일주일 치 정도 한번에 올린적이 있었는데, 그 때는 CSV 파싱 오픈 소스 이용해서, 읽어 들이고, 만건씩 AddBatch 사용해서 커밋 했었습니다. 2천 만건 기준으로 약 6분 정도 소요되었던 것으로 기억합니다. 이전 프로젝트 중에 하루 교통카드데이터(서울기준, 일 하루 약 이천만건)를 일주일 치 정도 한번에 올린적이 있었는데, 그 때는 CSV 파싱 오픈 소스 이용해서, 읽어 들이고, 만건씩 AddBatch 사용해서 커밋 했었습니다. 2천 만건 기준으로 약 6분 정도 소요되었던 것으로 기억합니다. |
2014-11-19 15:02:29 [덧글] |
추천 java nio 써서 버퍼사이로 끊어서 리드하세요 |
http://www.ppomppu.co.kr/zboard/view.php?id=developer&no=14020
String pram = new String(request.getParameter("param").getBytes("8859_1"),"euc-kr");
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
java.net.URLDecoder.decode(request.getParameter("param"), "EUC-KR");
1. http://mamoru15.egloos.com/1815962
2. http://cafe.naver.com/javababy.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=68&
< 역컴파일러 사용법 >
1. 역컴파일하려는 class파일이 있는 폴더에 jad.exe를 옮긴다.
2. DOS 창을 열고 그 폴더를 찾아간다.
3. jad -sjava '클래스명' 을 타이핑한다.
(예) 경로가 c:javafiles, 역컴파일하려는 파일이 A.class인 경우 DOS창에서
C:javafiles> jad -sjava A.class
4. 확인해 보면 그 폴더에 A.java 파일이 생성되어 있을 것이다. jad.exe
5. 여러 파일을 한번에 컴파일
jad -sjava ./경로폴더명/*.class
(예) jad -sjava ./temp/*.class
좋은 정보 감사합니다.. ^^
--------------------------------------------------------------------------------------------------------------
문제:
독립적인 기능을 하는 자바스크립트 클래스를 하나 만들었다.
이 클래스를 싱글턴으로 사용하고 싶다.
또한, 일반적인 getInstance() 라는 스태틱 메서드를 사용하는 대신,
바로 생성자를 호출해서 객체를 만들도록(내부적으로 싱글턴 인스턴스를 리턴) 하려고 한다.
어떻게 하면 자바스크립트에서 생성자만으로 싱글턴을 구현할 수 있을까?
해결책:
한참을 고민해봤다.
이런 방법으로 구현해보면 어떨까?
클래스의 생성자에 대한 유효범위를 제한해두고,
그 유효범위 안에서 window 속성의 클래스 생성자(래퍼)를 다시 정의한다.
(일종의 프록시 패턴이라고 할 수도 있겠다)
window 속성의 클래스 생성자에서는 클래스의 유일한 인스턴스를 만들어 리턴하도록 싱글턴을 구현한다.
즉, 외부에서 클래스 생성자를 호출해서 객체를 만들 때에는,
window 속성의 생성자를 호출하게 되므로 생성자만으로 싱글턴을 구현할 수 있다.
아래 실제 구현 예제를 이해하기 위해 몇 가지 자바스크립트 언어에 대한 선수 지식이 필요하다.
javascript 언어 특징 보기
var test = 'aaa';
alert(window.test == test); // 전역 변수인 test 는 window.test 와 동일하다.
2. javascript 의 변수 유효 범위(scope)는 블럭 단위가 아니라 함수 단위이다.
function a() {
if (true) {
var x = 'aaa';
}
alert(x); // 일반적인 언어와 달리 javascript 의 유효범위는 if, for 등의 블럭단위가 아니라, 함수단위다.
// 그렇기 때문에 여기서도 x 는 정상적으로 aaa 를 출력한다.
}
alert(x); // 함수 밖에서는 x 가 정의되어 있기 때문에 에러를 발생한다.
3. 유효 범위를 제한하기 위해 익명함수를 정의할 수 있다.
익명함수 function() {} 를 사용하면 임의대로 유효범위를 제한할 수 있다.
2번의 예제에서 if 블럭 내에 익명함수를 정의해 아래와 같이 유효범위를 제한할 수 있다.
function a() {
if (true) {
// 스스로를 실행하는 익명함수를 정의한다. (function(){ })(); 와 같은 형태이다.
(function() {
var x = 'aaa'; // 이제 x 의 범위는 익명함수 내로 제한됐다.
})();
}
alert(x); // 여기서 x 에 접근할 수 없다.
}
4. 클로저를 통해 외부 함수에서 이미 종료된 함수 내의 변수에 접근할 수 있다.
javascript 의 클로저를 통해 이미 종료된 함수의 변수에 대한 참조를 가질 수 있다.
아래 예제를 보면 쉽게 이해할 수 있을 것이다.
function a() {
var x = 'aaa';
setTimeout(function() { // 이 함수는 a() 함수가 종료된 후, window scope 에서 실행된다.
alert(x); // a() 함수는 종료되었지만, 클로저를 통해 x 의 참조를 유지할 수 있다.
// 여기서는 aaa 가 정상적으로 출력된다.
}, 1000);
}
간단한 실제 구현 모습은 아래와 같다.
(function() { // (a) 익명함수로 유효범위를 제한한다.
// (b) window 속성의 생성자를 정의한다.
// 외부에서 생성자를 호출할 때에는 이 생성자가 호출된다.
window.Singleton = function(name) {
// (d) 객체가 생성되어 있지 않을 경우, 새 객체를 _instance 에 담는다.
if (_instance == null) {
_instance = new Singleton(name);
}
return _instance;
}
// (c) 유일한 싱글턴 인스턴스
var _instance = null;
// (e) 실제 객체 생성자
// 외부에서 이 생성자에 접근할 수 없다.
var Singleton = function(name) {
this.name = name;
}
})();
실제 객체의 생성자는 (e) 부분이다.
외부에서 이 객체의 생성자에 접근하지 못하도록 익명함수(anonymous function)로 생성자를 감싸준다.
이 부분이 (a) 부분이 되겠다.
(c) 와 같이 유일한 싱글턴 인스턴스를 담을 변수를 선언해준다. 전역변수와 같이 선언되어 있지만,
이 변수 역시 생성자와 같은 유효범위에 있기 때문에 외부에서는 접근하지 못한다.
이제 외부에서 이 객체를 생성할 수 있도록 (b)와 같이 window 속성에 같은 이름으로 생성자를 구현해준다.
window.Singleton 은 window 속성이기 때문에 외부에서도 호출이 가능하며,
window prefix 없이 바로 new Singleton() 과 같이 호출이 가능하다.
(외부에서 new Singleton(); 을 호출했을 때 (e) 부분의 실제 함수가 호출되는 게 아니다.)
또한, window.Singleton 객체가 유효범위 밖에서 _instance 를 참조할 수 있는 건, 클로저(closure) 때문이다.
window 속성의 객체 생성자(b)에서는 (d)에서와 같이 싱글턴 부분을 구현한다.
_instance 가 존재하지 않을 경우, 새 객체를 생성해 할당하고, 그렇지 않을 경우 이미 존재하는 객체를 리턴한다.
이와 같이 구현하면 외부에서 new Singleton() 을 호출했을 때,
(실제 객체의 wrapper 격인) window.Singleton() 생성자가 호출되며,
window.Singleton 생성자 내에서 싱글턴 객체를 리턴하게 된다.
아래와 같이 호출해보면, new Singleton() 을 통해 싱글턴 객체가 생성되는 것을 확인할 수 있다.
// 여기서 호출되는 Singleton 생성자는 window.Singleton 이다.
var s1 = new Singleton('aaa');
alert(s1.name); // aaa 출력
var s2 = new Singleton('bbb'); // 이미 생성된 객체가 리턴된다.
alert(s2.name); // aaa 출력
다소 번거롭긴 하지만, 이 방법을 통해 실제 생성자에 대한 접근을 제한할 수 있고,
new 키워드를 통해 싱글턴 객체를 리턴하도록 구현할 수 있다.
(굳이 패턴으로 나누어 생각하려고 한다면, 싱글턴 + 프록시 패턴 정도겠다. )
일반적인 패턴처럼, getInstance() 를 통해 싱글턴 객체를 생성하고 싶을 경우,
같은 방법으로 익명함수 내에서 window.Singleton 안에 getInstance() 를 정의해주면 되겠다.
기타 다른 방법들도 있지만,
기존 생성된 객체를 유지하는 한도 내에서 유용하게 사용할 수 있을 것이라 생각한다.
* 참고:
검색해보니 싱글턴을 구현하는 다른 방법들에 대한 좋은 자료들이 많다.
1. How to make a singleton in javascript
가장 알아보기 쉽고 기본적인 방법이니 참고해보도록 하자.
constructor 이름을 통해 이미 만들어진 객체를 싱글턴으로 만드는 전역 메서드 구현에 대한 방법도 있다.
좋은 내용이 많으니 꼭 읽어보자.
그 외,자바스크립트의 유효범위와 클로저에 대해 잘 이해가 안될 경우, 아래 포스트를 참고하자.
우선 글을 쓰기에 앞서 해당 구현방법은 철저히 개인적인 방법임을 밟히는 바이다.
(이방법이 최적의 방법 혹은 정석이 될수 없음을 미리 알리는 바이다.)
요즘 네이버나 다음과 같은 여러 포탈 사이트에서 오픈API를 제공하고 있다.
거의 모든 오픈API의 방식이 개인키를 부여하여 특정주소를 요청하면 XML로 결과를 리턴해주는 방식이다.
AJAX 에서의 자바스크립트는 기본적으로 XMLHttpRequest 객체의 open()메소드와 send()메소드를 이용해서 해당 uri의 xml을 요청할 수 있다.
하지만 자바스크립트의 보안제약사항에 따라 XMLHttpRequest 객체는 해당 페이지를 로딩한 서버 외에는 연결할 수 없게 되어있다.
그래서 오픈API요청을 자바스크립트에서 바로 사용할수가 없다.
이것을 해결하기 위해선 여러방법이 있겠지만 그중에서 나는 서버에서 해당 오픈API요청을 대신하고 결과를 보여주는 프록시페이지를 만들어 보기로 했다.
해당 구현방법을 알아보기위해 인터넷의 넓은 바다를 항해한 결과,
아파치 자카르타 프로젝트의 commons HttpClient API를 이용하여 구현하는 방법을 찾게 되었다.
모든 구현을 완료하고 페이지를 실행해보았지만 문제가 생겼다.
좀더 삽질을 하고난후 commons logging API 와 commons codec API도 필요하다는것을 알게되었다.
(logging, codec 이 왜 필요한지는 아직 알수없었다. 디버깅과정에서 ClassNotFound 가 뜨는 것들을 추가해주다가 알게되었다.)
(초보의 무식함이다 =..= 만약 아직도 안되고있었다면 계속 삽질을? ;;;)
proxy.jsp
<%@ page import="org.apache.commons.httpclient.HttpClient" %>
<%@ page import="org.apache.commons.httpclient.methods.GetMethod" %>
<%@ page import="org.apache.commons.httpclient.HttpStatus" %>
<%
request.setCharacterEncoding("utf-8");
String url = "오픈API요청URI";
HttpClient client = new HttpClient();
GetMethod method = new GetMethod(url);
try{
int statusCode = client.executeMethod(method);
out.clearBuffer();
response.reset();
response.setStatus(statusCode);
if(statusCode == HttpStatus.SC_OK){
String result = method.getResponseBodyAsString();
response.setContentType("text/xml; charset=utf-8");
out.println(result);
}
}finally{
if(method != null) method.releaseConnection();
}
%>
나는 네이버 실시간 음악 순위를 리턴받아 보았다.
(get방식으로 파라미터값을 받을수있도록 jsp를 구현한 상태이다.)
http://cyzest.cafe24.com/open/rank/proxy_rank.jsp?query=music
이제 자바스크립트의 XMLHttpRequest 객체에서의 open() 메소드에서는 오픈API주소가 아닌 proxy.jsp 의 주소를 요청하면 되는 것이다.
http://cyzest.cafe24.com/open/rank
이제 해당 오픈API의 결과를 유용하게 이용하도록 구현하는 것은 사용자의 몫이 될것이다. :)
iBATIS는 공식적으로 캐시를 할때에 OSCache를 사용할 수 있도록 되어있습니다. 하지만 그 기능이 매우 자동적이며 제한적이고 세세한 설정을 개발자가 할수가 없습니다. 그래서 다음을 한번 알아 보기로 할까요. 다음의 예시는 [이곳]에 언급된 내용을 살짝 수정하였습니다.
<cacheModel type="OSCACHE" id="cacheModel" readOnly="true">
<flushInterval hours="24"/>
<flushOnExecute statement="flushCache"/>
</cacheModel>
<resultMap class="kr.pe.theeye.Cache" id="CacheResult">
...
</resultMap>
<insert id="flushCache" resultClass="string">
INSERT ...
</insert>
<select id="makeCache1" resultMap="CacheResult" cacheModel="cacheModel">
SELECT ...
</select>
<select id="makeCache2" resultMap="CacheResult" cacheModel="cacheModel">
SELECT ...
</select>
<select id="makeCache3" resultMap="CacheResult" parameterClass="int"
cacheModel="cacheModel">
SELECT ... WHERE PAGE = #value#
</select>
위의 SQL맵 예제에서는 INSERT문 한개와 SELECT문 3개가 존재합니다. 모두 cacheModel이라는 id의 캐시모델과 연관되어집니다. 이것을 간단하게 그림으로 그려보면 다음과 같은 모양을 가지고 있습니다.
그려놓고 보니깐 좀 말이 안되는 그림 같아 보이네요;; 아무튼 하나의 캐시 모델에 3가지의 캐시를 생성할 수 있는 조건이 있고 2가지 캐시를 삭제할 수 있는 조건이 있다고 봐주시면 되겠습니다. 둥근 사각형은 개발자가 임의로 호출을 해야만 하는 기능들이고 위의 동그라미는 캐시 유지 시간 설정으로 봐주시면 되겠습니다.
이제 다음의 몇가지 예시 상황들에 대한 캐시의 처리 과정에 대해 알아보겠습니다.
1. 한개의 캐시 처리 (makeCache1 → flushCache)
makeCache1이 수행되면 cacheModel에 하나의 캐시가 생성됩니다. 앞으로 makeCache1이 호출될때마다 캐시가 존재하는한 DB에 접근없이 캐시결과값을 제공하게 됩니다. flushCache를 수행하면 캐시가 삭제됩니다. 다시 makeCache1을 호출하면 DB에서 결과를 가져와서 반환함과 동시에 캐시를 생성하게 됩니다. 캐시가 생성된 시점에서 flushInterval에 설정된 시간이 경과하도록 flushCache가 호출되지 않는다면 시간 만료로 자동 삭제됩니다.
2. 두개의 캐시 처리 (makeCache1 → makeCache2 → flushCache)
makeCache1이 호출되면 cacheModel에 하나의 캐시가 생성됩니다. makeCache2가 호출되면 마찬가지로 cacheModel에 또다른 하나의 캐시가 생성됩니다. 이 두개의 캐시는 엄연히 다르며 각각의 makeCacheX가 호출될때 해당하는 만들어진 캐시값을 반환하게 됩니다. 하지만 둘다 모두 동일하게 cacheModel안에 소속됩니다. 이어서 flushCache를 호출하게 되면 두 캐시가 모두 삭제됩니다. 정확히는 flush에 대한 설정을 해두면 해당 캐시모델의 모든 캐시를 소거한다고 보시면 됩니다. 그러므로 이런 부분에 주의하여 캐시모델을 함께 사용할지 따로 다른 캐시모델을 만들지를 결정하셔야 합니다.
3. 인자값의 차이에 따른 처리 (makeCache3[1] -> makeCache3[2] -> flushCache)
makeCache3에는 parameterClass를 사용하여 동적인 쿼리를 수행하도록 되어있습니다. 예시로 간단하게 int값을 받도록 하였는데요. 캐시를 생성할때의 키값에는 이 인자값들이 모두 포함되어 키를 이룹니다. 그러므로 paramterClass로 넘어오는 값이 1일때와 2일때는 다른 쿼리(키)가 됩니다. 그러므로 1이라는 값의 인자를 받아 실행되는 makeCache3의 캐시와 2라는 값을 받아 실행되는 makeCache3는 각각 별개의 캐시가 생성됩니다. 마찬가지로 하나의 cacheModel안에서 호출되지만요. flushCache를 호출하면 이 두캐시가 모두 삭제됩니다.
결론을 내보자면 위와 같은 iBATIS에서 제공하는 기본적인 캐시모델로는 같은 쿼리지만 다른 결과가 나올 수 있는 부분에는 사용할 수 없습니다. SNS 서비스에서 볼 수 있을 다음을 생각해 봅시다.
2. 사용자가 접속하여 친구들의 최근근황을 확인하였다. [캐시 생성됨]
3. 친구중 한명이 최근 근황을 업데이트 하였다.
4. 사용자가 다시한번 친구들의 최근근황을 확인하였다. [캐시값 반환됨]
위를 수행하였을 때 친구들의 정보가 바뀌어도 사용자는 계속 캐시된 값을 받게됨을 알 수 있습니다. 그러므로 친구들의 업데이트 된 정보를 적시에 얻기가 힘듭니다. 하지만 그렇다고 친구의 정보가 업데이트 될때 다른 사용자의 캐시를 삭제하는데도 무리가 있습니다. 왜냐하면 캐시 키 값을 모르기 때문이죠. 이부분을 해결하려면 iBATIS의 SQL맵 캐시 기능을 사용하지 말고 자체적인 알고리즘으로 구현을 해야 할 것 같습니다.
승용이가 오라클 프로시저, 함수 공부하면서 오라클의 substr()함수와 같은 기능을 하는 함수를 기존 함수를 사용하지 않고 만드는 방법을 물어본건데...다른 방법으로는 죽어도 모르겠고(혹시 아시는 분 여기에 좀 올려주세요)...오라클에서 자바로 구현이 가능(자바말고. pro c등 기타언어로도 가능하지만..우리가 자바개발자인고로..)하기에 이렇게 만드는 방법을 올립니다. 개발시 참고바랍니다.
Java Stored Procedures or Functions
오라클에서 사용자 정의 함수나 프로시저를 만드는 것은 우리같은 자바 개발자에겐 조금 짜증나는 작업이다. 만약, 자바로 함수를 간단하게 만들 수 있다면...당근 가능하다...^^...
비즈니스단이나 뷰어단에서 해야할 일을 DB단이 처리함으로써 응용프로그램이나 미들웨어에 작업하는 것보다 빠른 속도를 얻으면서 간단하고 쉽게 제작 사용할 수 있을 것이다.
자바를 이용한 오라클 함수를 만드는 방법을 예제와 함께 정리했으니 따라하며 이해하시길...
예제) 오라클의 substr()함수과 같은 자바클래스를 이용한 사용자 정의 함수 작성 방법
1. Java 클래스 제작(Writing the Java Classes)
데이터베이스의 테이블들을 생성한 후, 어떠한 기능(메서드)들이 구현되어져야 하는지
생각하고 기능들을 메서드로서 구현한다.
import java.sql.*;
import java.io.*;
public class SubStr_ex
{
public static String cutString(String str_in, int start_no, int cut_no)
{
cut_no = start_no + cut_no - 1;
char[] tmp = str_in.toCharArray();
String out_str = "";
if(cut_no>str_in.length()) cut_no = str_in.length();
for(int i=1;i<=str_in.length();i++){
if(i>= start_no && i <= cut_no){
out_str = out_str+tmp[i-1];
}
}
return out_str;
}
}
2. 자바클래스 오라클에 올리기(Loading the Java Classes)
자바클래스를 작성한 후 loadjava라는 오라클의 유틸리티를 이용하여 cmd창에서 다음과 같이
올려준다
D:\>loadjava -u scott/tiger@127.0.0.1:1521:ora -v -r -t SubStr_ex.java
arguments: '-u' 'scott/tiger@127.0.0.1:1521:ora' '-v' '-r' '-t' 'SubStr_ex.java'
creating : source SubStr_ex
loading : source SubStr_ex
resolving: source SubStr_ex
<옵션>
-v : verbose모드 옵션
-r : 컴파일하고 클래스안에 외부참조를 포함한다는 옵션
-t : 클라이언트 측 JDBC Thin 드라이버를 이용해서 데이터베이스에 연결한다는 옵션
3. load된 자바클래스를 이용한 오라클 함수 만들기(Publishing the Java Classes)
자바클래스를 올렸으면 이제 오라클 함수를 만들어 사용하자
sqlplus창에서 아래와 같이 실행하자
CREATE OR REPLACE FUNCTION SubStr_ex(str_in VARCHAR2, sn NUMBER, en NUMBER)
RETURN VARCHAR2
AS
LANGUAGE JAVA
NAME 'SubStr_ex.cutString(java.lang.String, int, int) return java.lang.String';
/
그러면 substr_ex()라는 사용자 함수가 생성되어져 있을 것이다.
4. 이제부터는 맘대로 사용....(Calling the Java Stored Procedures or Functions)
SQL> select substr_ex('asdfghhj',3,2) from dual;
SUBSTR_EX('ASDFGHHJ',3,2)
-------------------------------------------------------------------------------
df
더 많은 정보는
http://download-west.oracle.com/docs/cd/B19306_01/java.102/b14187/cheight.htm#CHDCCHID
이곳을 참고 바랍니다.
그리고..오라클과 자바...타입 매핑
Table 6-1 Legal Data Type Mappings
SQL Type | Java Class |
---|---|
CHAR , LONG , VARCHAR2 |
oracle.sql.CHAR
|
DATE |
oracle.sql.DATE
|
NUMBER |
oracle.sql.NUMBER
|
OPAQUE |
oracle.sql.OPAQUE |
RAW , LONG RAW |
oracle.sql.RAW
|
ROWID |
oracle.sql.CHAR
|
BFILE |
oracle.sql.BFILE |
BLOB |
oracle.sql.BLOB
|
CLOB , NCLOB |
oracle.sql.CLOB
|
OBJECT
Object types |
oracle.sql.STRUCT
|
REF
Reference types |
oracle.sql.REF
|
TABLE , VARRAY
Nested table types and |
oracle.sql.ARRAY
|
any of the preceding SQL types | oracle.sql.CustomDatum
|
2006.4.27 / 무찬아빠(naeodud@nate.com)
ResourceBundle 사용하기
클래스 상속관계 java.lang.Object - java.util.ResourceBundle
ResourceBundle은 다음과 같은 특징을 같습니다.
- 다른 국가의 언어에 맞추어 로컬라이징을 할 수 있습니다.
- 복수의 로케일을 동시에 처리할 수 있습니다.
- 새로운 로케일의 추가가 쉽습니다.
ResourceBundle을 사용하기 위해서는 다음과 같이 getBundle() 메서드를 사용하여 ResourceBundle 클래스를 로드합니다.
ResourceBundle myResource = ResourceBundle("MyResource", 로케일);
로케일의 생략시 현재 사용하는 로케일이 지정됩니다. 한글은 'ko_KR'로 로케일이 지정됩니다. 'MyResource'의 이름은 베이스 네임으로 로케일에 따라 자동으로 베이스 네임과 함께 로케일 이름이 붙은 이름을 먼저 찾게 됩니다. 예를 들면 로케일이 'ko_KR'인 경우 'MyResource_ko_KR'로 확장되며, 이 파일이 존재하지 않는 경우, 'MyResource_ko.properties'를 찾고 이 파일 존재하지 않으면 최종적으로 'MyResource_ko_KR.properties'이라는 리소스 파일을 현재 클래스가 위치한 패키지 내에서 찾게 됩니다. 만일 이런 이름의 파일이 없을 경우는 'MyResource.properties' 파일을 찾게 됩니다.
package net.jeongsam.extra; import java.util.Iterator; import java.util.ResourceBundle; import java.util.Set; public class ResourceLocaleEx01 { public static void main(String[] args) { // net.jeongsam.extra 패키지에서 MyResource_ko_KR.properties 파일을 탐색한다. ResourceBundle myResource = ResourceBundle.getBundle("net.jeongsam.extra.MyResource"); Setkeys = myResource.keySet(); Iterator itKeys = keys.iterator(); while (itKeys.hasNext()) { String key = itKeys.next(); System.out.println(key + "=" + myResource.getString(key)); } } }
ROME이 여러모로 좋은 오픈소스로 활용되는 것 같네요. 좀더 분석해서 필요한 기능들을 공유할께요.
필요한 라이브러리는 ROME 1.0, JDOM 1.0이 필요합니다.
1. Feed 생성 소스
package client;2. Servlet 소스
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.mimul.DataFacade;
import com.mimul.domain.Category;
import com.mimul.domain.Entry;
import com.mimul.domain.SiteConfig;
import com.sun.syndication.feed.synd.SyndCategory;
import com.sun.syndication.feed.synd.SyndCategoryImpl;
import com.sun.syndication.feed.synd.SyndContent;
import com.sun.syndication.feed.synd.SyndContentImpl;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndEntryImpl;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.feed.synd.SyndFeedImpl;
public class CreateFeed
{
private final Log log = LogFactory.getLog(this.getClass());
private DataFacade dataFacade;
public void setDataFacade(DataFacade dataFacade) {
this.dataFacade = dataFacade;
}
public SyndFeed getFeed(String type)
{
SyndFeed feed = null;
Entry post = null;
List<Entry> recentEntries = null;
List<SyndEntry> entries = null;
List<SyndCategory> categories = null;
StringBuilder sb = null;
try {
feed = new SyndFeedImpl();
feed.setFeedType(type); //rss_2.0
feed.setTitle("Mimul's Developer World");
feed.setLink("http://www.mimul.com/");
feed.setDescription("Java Ecxamples Code");
entries = new ArrayList<SyndEntry>();
post = new Entry();
post.setType("post");
post.setEntryStatus("publish");
// 데이터 베이스에 저장된 엔트리를 가져온다
recentEntries = dataFacade.getEntryPage(post,
10, 0, null, null).getItems();
for (Entry entry:recentEntries) {
SyndEntry syndEntry = new SyndEntryImpl();
syndEntry.setTitle(entry.getTitle());
String link = null;
if(StringUtils.isNotBlank(entry.getName())) {
link = "http://www.mimul.com" + "/post/" +
entry.getName() + ".html";
}else{
link = "http://www.mimul.com" + "/post/id/" +
entry.getId() + ".html";
}
syndEntry.setLink(link);
syndEntry.setAuthor(entry.getAuthor().getNickname());
syndEntry.setPublishedDate(entry.getPostTime());
categories = new ArrayList<SyndCategory>();
// 카테고리 정보를 가져온다
for(Category category:entry.getCategories()) {
SyndCategory syndCategory = new SyndCategoryImpl();
syndCategory.setName(category.getName());
syndCategory.setTaxonomyUri("http://www.mimul.com" +
"/category/" + category.getName() + "/");
}
syndEntry.setCategories(categories);
SyndContent content = new SyndContentImpl();
content.setType("text/html");
sb = new StringBuilder();
sb.append(entry.getSummary())
.append("<p>").append("<a href=\"").append(link)
.append("\">[more..]</a></p>");
content.setValue(sb.toString());
syndEntry.setDescription(content);
entries.add(syndEntry);
}
feed.setEntries(entries);
} catch (Exception e) {
log.error(e);
}
return feed;
}
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
SyndFeedOutput output = null;
try {
SyndFeed feed = getCreateFeed().getFeed("rss_2.0");
feed.setFeedType("rss_2.0");
response.setContentType("application/xml; charset=UTF-8");
response.setHeader("Pragma","No-Cache");
response.setHeader("Cache-Control","No-Cache");
response.setDateHeader("Expires",0);
output = new SyndFeedOutput();
output.output(feed, response.getWriter());
} catch (FeedException e) {
log.error(e);
response.sendError(
HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"FEED 생성 오류");
}
}
private CreateFeed getCreateFeed() {
return (CreateFeed) WebApplicationContextUtils.
getWebApplicationContext(getServletConfig()
.getServletContext()).getBean("createFeed");
}
Struts2에서 Action의 기본확장자는 action이다.
REST의 관점에서 보면 URL은 Resource를 가리키고 있을 뿐이어서 action이란 접미어가 붙는 것은 restful 하지 않아 보인다.
그래서 struts.properties의 struts.action.extension에 아무것도 할당하지 않으면 간단히 확장자를 제거 할 수 있다.
struts.action.extension=
struts.properties
하지만, 이렇게 하면 welcome-file을 사용할 수 없고, *.jsp, *.gif, *.jpg등 struts action이 아닌 resource들은 접근 할 수도 없다. 그래서 다음과 같이 간단한 필터를 만들어 보았다.
- package filters;
- import java.io.IOException;
- import javax.servlet.Filter;
- import javax.servlet.FilterChain;
- import javax.servlet.FilterConfig;
- import javax.servlet.ServletException;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import javax.servlet.http.HttpServletRequest;
- import org.apache.struts2.dispatcher.FilterDispatcher;
- public class StrutsRedirectFilter implements Filter {
- FilterDispatcher dispatcher = new FilterDispatcher();
- @Override
- public void doFilter(ServletRequest req, ServletResponse res,
- FilterChain chain) throws IOException, ServletException {
- HttpServletRequest request = (HttpServletRequest) req;
- String uri = request.getRequestURI();
- // 확장자가 있는 경우
- if (uri != null
- && (uri.equals(request.getContextPath().concat("/")) || uri
- .substring(uri.indexOf("/")).indexOf(".") > -1)) {
- chain.doFilter(req, res);
- // 아닌 경우 struts로 넘긴다.
- } else {
- dispatcher.doFilter(req, res, chain);
- }
- }
- @Override
- public void destroy() {
- dispatcher.destroy();
- }
- @Override
- public void init(FilterConfig config) throws ServletException {
- dispatcher.init(config);
- }
- }
StrutsRedirectFilter.java
이 필터를 org.apache.struts2.dispatcher.FilterDispatcher 대신 web.xml 에 넣어주면 된다.
- <?xml version="1.0"?>
- <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
- "http://java.sun.com/dtd/web-app_2_3.dtd">
- <web-app>
- <display-name>My Application</display-name>
- <filter>
- <filter-name>struts2</filter-name>
- <filter-class>filters.StrutsRedirectFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>struts2</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- </web-app>