flowerspring

logroate란 일정 시간을 주기로 원본 로그 파일을 일, 주, 월 단위로 쪼개 저장할 수 있도록 도와주는 프로그램이다.
이렇게 하는 이유는 하나의 로그 파일을 하나의 파일로 저장하게 되면 데이터 용량이 기하급수적으로 쌓이게되면 서버에 로그 파일로 용량이 꽉차 서버가 해야할 다른 서비스를 제대로 제공하지 못하는 경우가 발생한다.
특정 단위로 쪼개어 저장하게 되면 주기적으로 삭제하면 서버 용량도 효율적으로 관리할 수 있으니 리눅스 서버 관리자라면 한번쯤 알아볼 유용한 기능이다.

1. 사용법
 
사용법은 비교적 간단하다. 
/etc/logrotate.d 경로에 로그 설정 파일을 만들어서 해당 위치에 옮겨 두기만 하면 cron의 /etc/cron.daily, /etc/cron.weekly등의 실행주기에 의해서 자동으로 실행된다.


2. 설정 파일
설정 파일은 해당 로그 파일을 생성한 후 vi 편집기를 이용하여 작성 한 후 /etc/logrotate.d에 옮겨 놓으면 된다.    
설정 방법은 로그 파일의 경로를 지정하고 관련된 옵션을 지정한다.
다음은 아파치의 로그 파일을 logrotate를 이용하여 설정한 예제다.

/etc/logrotate.d/httpd
/app/web/apache24/logs/*error_log
/app/web/apache24/logs/*access_log{
        daily
        create 0600 testuser01 testusers
        rotate 90
        dateext
        missingok
        notifempty
        copytruncate
        nocompress
}



3. 옵션


 옵션

설명 

 copy

로그 파일의 카피를 생성한다. 이 옵션을 사용하면, 이미 낡은 로그 파일이 존재하게 되므로, create 옵션은 무효가 된다.

 copytruncate

카피의 작성 후에 로그 파일을 이동하는 대신에, 오리지날의 로그 파일을 아무것도 저장하지 않는 로그로 다시 만든다.

어느 프로그램이 로그 파일을 닫지 못하고, 언제까지 전의 로그 파일에 계속 쓰고 있는 경우 등에 유효한 옵션이다. 다만 파일을 카피하는 순간의 로그가 소실되는 가능성이 높다 .


이 옵션을 넣지 않으면 현재 사용중인 로그를 다른이름으로 move하고 새로운 파일을 생성한다.

 extention

로테이션 후의 백업 로그에 부가하는 확장자(extension)를 지정한다. ext에는 파일의 확장자를 지정한다. compress 옵션을 지정했을 경우에는, 여기서 지정한 확장자(extension)의 뒤에 gz등의 확장자(extension)가 부가된다. 

 ifroate

 만약 로그 파일이 공백이었다고 해도 로테이션 시킨다.

디폴트로 유효하게 되어 있다.

mail 

 로그 파일의 로테이션이 완료한 시점에서, mail 옵션의 인수로서 설정된 메일 주소에 메일을 보낸다.

 nomail

 메일로 통보하지 않는다.

 maillast

mail 옵션 사용시에 로테이션 한 파일을 메일로 출력한다. 

 mailfirst

mail 옵션 사용시에 로테이션 할 예정의 파일을 메일로 출력한다. 

 missingok

로그 파일이 발견되지 않았던 경우, 에러 메세지를 출력하지 않고 다음의 로그 파일의 처리로 이행한다. 

 size

size 옵션의 파라미터로서 준 수치보다 로그 파일의 사이즈가 클 경우, 로그 파일의 로테이션을 실시한다. 

"M"(메가), "K"(킬로)를 지정할 수 있다.

 start

 로테이션 하는 경우에 부가되는 숫자의 확장자를 몇부터 시작하는지를 지정한다. 디폴트에서는 1 이다.

 weekly

 매주 로그를 바꾼다.

 daily

매일 로그를 바꾼다. 

 monthly

 월 단위로 로그를 바꾼다.

 roate 4

 로그를 4세대분 남긴다. weeky를 지정할 경우 4주간의 로그라는 의미이다.

 maxage [n]

 [n]일 이상이 된 로그 파일을 삭제한다.

 create

 (비어있는)신규 로그 파일을 로테이션 한 직후에 작성한다.

 compress

압축한다. 디폴트에서는 압축하지 않게 되어 있으므로, 디스크의 빈 용량을 보고, 유효하게 할지 지정한다. 

 nocompress

압축하지 않는다.

 notifempty

 log 내용이 없으면 rotation 하지 않는다.

 ifempty

 로그파일이 비어있어도 로테이트한다.

 prerotate-endscript

사이의 명령어를 로그파일 처리전에 실행한다. 

 postrotate-endscript

사이의 명령어를 로그파일 처리 후에 실행한다. 

 errors [mail address]

로테이트 실행시 에러가 발생하면 이메일로 통보한다. 



Comment +0

Oracle Sql Developer Data Modeler 툴을 사용하기 전에 설정하는 정보다.


1. 언어 변경


Oracle Sql Developer Data Modeler를 한글로 설정하면 엔티티의 속성이 제대로 반영되지 않는 경우가 있다. 이를 해결 하기 위해서는 영문으로 변경하여 사용한다.

영문으로 변경하는 방법은 "datamodeler/datamodeler/bin/datamodeler.conf" 파일을 편집기로 연 후 맨 아래에 아래 항목을 입력한 후 저장한다.

 

AddVMOption -Duser.language=en

AddVMOption -Duser.country=US


영문으로 바꿔도 한글 사용은 가능하다.

Comment +0


리눅스로 쉘을 실행할때 특정 계정으로 실행해야 할 때가 있다.

사용법은 아래와 같다    

su - 아이디 -c "명령어1; 명령어2; 명령어3"


예를 들어서 tester 계정을 이용하여 jar 파일을 실행한다고 하자. 그럴경우는 다음과 같이 사용한다.

su - tester -c "nohup java -jar /bin/dataservice-process.jar /tmp 2>> /dev/null >> /dev/null &"





Comment +0

maven jetty plugin 옵션 중에 scanIntervalSeconds 라는 옵션을 줘서 파일이 변경됬을 경우 일정시간 마다 서버를 리스타트 하는 기능이 있다.

이것과 비슷하게 지정된 특정 디렉토리에 파일이나 디렉토리가 생성, 삭제, 변경 되는 것을 모니터링 하는 클래스를 맨들어 보았다.

이것을 위해서 Jetty util 패키지에 있는 Scanner 클래스를 사용했다.

maven 사용자는 다음과 같은 dependency 를 추가한다.

  1. <dependency>
  2.     <groupId>org.eclipse.jetty</groupId>
  3.     <artifactId>jetty-util</artifactId>
  4.     <version>8.0.1.v20110908</version>
  5. </dependency>


maven 사용안하는 사람은 jetty-util-8.0.1.v20110908.jar 요파일을 클래스 패스에 추가 시켜 주면 된다. 

jetty-util-8.0.1.v20110908.jar


FileChangeScanner 클래스는 다음과 같다. DiscreteListener 클래스의 각각의 경우에 대해서 원하는 엑션을 취해주면 된다.

  1. package scanner;
  2.  
  3. import java.io.File;
  4. import java.io.FileFilter;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. import java.util.Timer;
  8. import java.util.TimerTask;
  9.  
  10. import org.eclipse.jetty.util.Scanner;
  11.  
  12. public class FileChangeScanner {
  13.     private Scanner scanner;
  14.  
  15.     public FileChangeScanner(String targetDir){
  16.         List<File> scanFiles = new ArrayList<File>();
  17.          
  18.         searchSubDirs(targetDir, scanFiles);
  19.          
  20.         scanner = new Scanner();
  21.         scanner.setScanInterval(1);          // 1초 간격으로 변경사항 스캔
  22.         scanner.setScanDirs(scanFiles);
  23.         scanner.setReportExistingFilesOnStartup(false);
  24.          
  25.         scanner.addListener(new Scanner.DiscreteListener() {
  26.             public void fileRemoved(String filename) throws Exception {
  27.                 System.out.println(filename + " is deleted");
  28.             }
  29.              
  30.             public void fileChanged(String filename) throws Exception {
  31.                 System.out.println(filename + " is changed");
  32.             }
  33.              
  34.             public void fileAdded(String filename) throws Exception {
  35.                 File f = new File(filename);
  36.                 if(f.isDirectory()) scanner.addScanDir(f);
  37.                  
  38.                 System.out.println(filename + " is added");
  39.             }
  40.         });
  41.     }
  42.      
  43.     public void start(){
  44.         try {
  45.             scanner.start();
  46.         } catch (Exception e) {
  47.             new RuntimeException(e);
  48.         }
  49.     }
  50.      
  51.     /**
  52.      * 하위 디렉토리들 찾기
  53.      * @param targetDir
  54.      * @param dirs
  55.      * @return
  56.      */
  57.     private List<File> searchSubDirs(String targetDir, final List<File> dirs){
  58.         File target = new File(targetDir);
  59.         target.listFiles(new FileFilter() {
  60.             public boolean accept(File file) {
  61.                 if( file.isDirectory() ) {
  62.                     dirs.add(file);
  63.                     searchSubDirs(file.toString(), dirs);
  64.                 }
  65.                 return false;
  66.             }
  67.         });
  68.          
  69.         return dirs;
  70.     }
  71.  
  72.     public static void main(String[] args) throws Exception{
  73.         // c:\test 하위 폴더 및 파일 변경 감시
  74.         new FileChangeScanner("c:\\test").start();
  75.          
  76.         // 실행하자 마자 프로그램이 종료되기 때문에 프로그램 종료방지를 위해 타이머 생성
  77.         // WAS 환경에서 돌릴때는 타이머 생성할 필요없음
  78.         Timer timer = new Timer();
  79.         timer.scheduleAtFixedRate(new TimerTask() {
  80.             public void run() {}
  81.         }60*100060*1000 );
  82.     }
  83. }


출처 : http://stove99.tistory.com/61

Comment +0

단순 참조일 경우

maven repository 가 없는 로컬 jar 파알을 maven 프로젝트에 추가 하기 위해서는 사설 repository를 만드는 방법도 있지만

다음과 같이 "dependency" 정의 시 scope 노드와 systemPath 노드를 사용하여 프로젝트에 포함된 jar 파일을 지정하여 줄 수 있다.

<dependency>
    <groupId>smack</groupId>
    <artifactId>smack-custom</artifactId>
    <version>1.0.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/smack-custom.jar</systemPath>
</dependency>

로컬을 Repository를 활용하는 방법

위와 같이 처리하는 경우는 문제점이 있다. scrop 의 system 이기 때문에 maven 빌드 시 해당 jar 파일이 포함되지 않는다.

이런 경우는 pom.xml 에 다음과 같이 정의하여 로컬을 repositoy로 활용하는 방법도 있다.

<dependency>
    <groupId>smack</groupId>
    <artifactId>smack-custom</artifactId>
    <version>1.0.0</version>
</dependency>
<repository>
	<id>in-project</id>
	<name>custom jars</name>
	<url>file://${project.basedir}/lib</url>
</repository>


이 때 ${project.basedir}/lib 는 maven 디렉토리 구조를 따르도록 구성해주어야 한다.

위의 예제에서는 디렉토리 및 파일명은 다음과 같이 구성해야 한다.

${project.basedir}/lib/smack/smack-custom/1.0.0/smack-custom-1.0.0.jar

만일 jenkins에서 maven 빌드를 하는 경우라면 다음과 같이 repository를 하나 더 추가해준다.

<repository>
	<id>in-project-jenkins</id>
	<name>custom jars-jenkins</name>
	<url>file://${JENKINS_HOME}/jobs/${JOB_NAME}/workspace/lib</url>
</repository>


출처 : http://itnp.kr/blog/post/Maven_Repository_%EA%B0%80_%EC%97%86%EB%8A%94_%EB%A1%9C%EC%BB%AC_jar_%ED%8C%8C%EC%9D%BC_%EC%9D%84_maven_project_%EC%97%90_%EC%B6%94%EA%B0%80%ED%95%98%EB%8A%94

'DEVELOPMENT' 카테고리의 다른 글

IDE 단축키  (0) 2018.11.28
로컬 jar 파일 maven porject에 추가하는 법  (0) 2017.04.25
hsqldb 사용하기  (0) 2016.07.23
windows에 있는 AppData 폴더란?  (0) 2015.07.30
Git  (0) 2013.12.30
dxf file format  (0) 2012.08.16

Comment +0

CentOS 7/RHEL 7부터는 방화벽을 데몬이 firewalld로 변경되었다. 방화벽 설정을 iptables 명령어 대신 firewall-cmd(콘솔), firewall-config(X-Windows) 명령어를 사용하여 설정한다. 


설치

  1. yum install firewalld
  2. systemctl start firewalld
  3. systemctl enable firewalld


설정

설정파일

/etc/firewalld/zones/public.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <zone>
  3.   <short>Public</short>
  4.   <description>For use in public areas. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accept
  5. ed.</description>
  6.   <service name="dhcpv6-client"/>
  7.   <service name="http"/>
  8.   <service name="ssh"/>
  9.   <service name="https"/>
  10. </zone>


재기동

service iptables restart 대신 아래 명령어 사용

firewall-cmd --rel


zone

사전에 정의된 zone 목록 출력

firewall-cmd --get-zones


전체 존 목록을 상세하게 출력

firewall-cmd --list-all-zones


기존 존  출력

firewall-cmd --get-default-zone


활성화된 존 출력

firewall-cmd --get-active-zone


서비스 목록

firewall-cmd --get-services


permanent로 등록된 서비스 목록

firewall-cmd --permanent --list-all


임의 포트 추가

--add-port=<portid> [-<portid>]/<protocol> 옵션을 사용하여 포트 추가

firewall-cmd --zone=public --add-port=8080/tcp


포트 삭제

--remove-port=<portid> [-<portid>]/<protocol> 옵션 사용

firewall-cmd --zone=public --remove-port=8080/tcp


rich-rule

firewall-cmd --permanent --zone=public --add-rich-rule="rule family="ipv4"  source address="192.168.10.0/24"  port protocol="tcp" port="9000" accept"


방화벽에 포트 추가

  1. firewall-cmd --permanent --zone=public --add-service=http
  2. firewall-cmd --permanent --zone=public --add-service=https

기본 zone은 public이므로 --zone=public 옵션은 생략 가능



출처 : https://www.lesstif.com/pages/viewpage.action?pageId=22053128#RHEL/CentOS7에서방화벽(firewalld)설정하기-설치



Comment +0

원문 : http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

번역 : 구글번역



소개(Intorduction)


한 번에 또는 대부분의 사용자는 SQL 데이터베이스에서 계층 적 데이터를 처리했으며 계층 적 데이터의 관리는 관계형 데이터베이스의 의도와 다르다는 것을 알게되었습니다. 관계형 데이터베이스의 테이블은 XML과 같은 계층 적 구조는 아니지만 단순한 플랫 목록입니다. 계층 적 데이터는 관계형 데이터베이스 테이블에서 자연스럽게 표현되지 않는 부모 - 자식 관계를가집니다.


우리의 목적을 위해 계층 적 데이터는 각 항목이 하나의 부모와 0 개 이상의 자식을 가진 데이터 모음입니다 (상위 항목이없는 루트 항목 제외). 계층 적 데이터는 포럼 및 메일 목록 스레드, 비즈니스 조직도, 콘텐츠 관리 범주 및 제품 범주를 비롯한 다양한 데이터베이스 응용 프로그램에서 찾을 수 있습니다. 우리의 목적을 위해 가상의 전자 제품 상점에서 다음과 같은 제품 카테고리 계층 구조를 사용합니다.




이 범주는 위에 인용 된 다른 예제와 동일한 방식으로 계층 구조를 형성합니다. 이 기사에서는 전통적인 adjacency list 모델부터 시작하여 MySQL의 계층 적 데이터를 다루는 두 가지 모델을 살펴 보겠습니다.




인접 목록 모델(The Adjacency List Model)


일반적으로 위에 표시된 예제 카테고리는 다음과 같은 테이블에 저장됩니다 (전체 CREATE 및 INSERT 문을 포함하므로 따라갈 수 있습니다).


CREATE TABLE category(
        category_id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(20) NOT NULL,
        parent INT DEFAULT NULL
);

INSERT INTO category VALUES(1,'ELECTRONICS',NULL),(2,'TELEVISIONS',1),(3,'TUBE',2),
        (4,'LCD',2),(5,'PLASMA',2),(6,'PORTABLE ELECTRONICS',1),(7,'MP3 PLAYERS',6),(8,'FLASH',7),
        (9,'CD PLAYERS',6),(10,'2 WAY RADIOS',6);

SELECT * FROM category ORDER BY category_id;
+-------------+----------------------+--------+
| category_id | name                 | parent |
+-------------+----------------------+--------+
|           1 | ELECTRONICS          |   NULL |
|           2 | TELEVISIONS          |      1 |
|           3 | TUBE                 |      2 |
|           4 | LCD                  |      2 |
|           5 | PLASMA               |      2 |
|           6 | PORTABLE ELECTRONICS |      1 |
|           7 | MP3 PLAYERS          |      6 |
|           8 | FLASH                |      7 |
|           9 | CD PLAYERS           |      6 |
|          10 | 2 WAY RADIOS         |      6 |
+-------------+----------------------+--------+
10 rows in set (0.00 sec)

인접성 목록 모델에서 테이블의 각 항목에는 상위 항목에 대한 포인터가 포함됩니다. 최상위 요소 (이 경우 electronics)는 상위 요소에 대해 NULL 값을가집니다. 인접 목록 모델은 아주 간단하다는 장점이 있으며, FLASH가 mp3 플레이어의 하위 항목 인 전자 제품의 하위 항목 인 휴대용 전자 제품의 하위 항목임을 쉽게 알 수 있습니다. 인접 목록 모델은 클라이언트 측 코드에서 상당히 쉽게 처리 할 수 있지만 모델로 작업하는 것은 순수 SQL에서 더 문제가 될 수 있습니다.



풀 트리 검색하기(RETRIEVING A FULL TREE)


계층 적 데이터를 처리 할 때 첫 번째로 공통적으로 수행해야 할 작업은 일반적으로 들여 쓰기의 형태로 전체 트리를 표시하는 것입니다. 이 작업을 수행하는 가장 일반적인 방법은 순수한 SQL에서 자체 조인을 사용하는 것입니다.


SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4
FROM category AS t1
LEFT JOIN category AS t2 ON t2.parent = t1.category_id
LEFT JOIN category AS t3 ON t3.parent = t2.category_id
LEFT JOIN category AS t4 ON t4.parent = t3.category_id
WHERE t1.name = 'ELECTRONICS';

+-------------+----------------------+--------------+-------+
| lev1        | lev2                 | lev3         | lev4  |
+-------------+----------------------+--------------+-------+
| ELECTRONICS | TELEVISIONS          | TUBE         | NULL  |
| ELECTRONICS | TELEVISIONS          | LCD          | NULL  |
| ELECTRONICS | TELEVISIONS          | PLASMA       | NULL  |
| ELECTRONICS | PORTABLE ELECTRONICS | MP3 PLAYERS  | FLASH |
| ELECTRONICS | PORTABLE ELECTRONICS | CD PLAYERS   | NULL  |
| ELECTRONICS | PORTABLE ELECTRONICS | 2 WAY RADIOS | NULL  |
+-------------+----------------------+--------------+-------+
6 rows in set (0.00 sec)


모든 잎사귀 찾기(FINDING ALL THE LEAF NODES)


LEFT JOIN 쿼리를 사용하여 트리에있는 모든 리프 노드 (자식이없는 노드)를 찾을 수 있습니다.


SELECT t1.name FROM
category AS t1 LEFT JOIN category as t2
ON t1.category_id = t2.parent
WHERE t2.category_id IS NULL;

+--------------+
| name         |
+--------------+
| TUBE         |
| LCD          |
| PLASMA       |
| FLASH        |
| CD PLAYERS   |
| 2 WAY RADIOS |
+--------------+



단일 경로 검색(RETRIEVING A SINGLE PATH)


자체 조인은 또한 우리가 계층 구조를 통해 완전한 경로를 볼 수있게 해줍니다 :


SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4
FROM category AS t1
LEFT JOIN category AS t2 ON t2.parent = t1.category_id
LEFT JOIN category AS t3 ON t3.parent = t2.category_id
LEFT JOIN category AS t4 ON t4.parent = t3.category_id
WHERE t1.name = 'ELECTRONICS' AND t4.name = 'FLASH';

+-------------+----------------------+-------------+-------+
| lev1        | lev2                 | lev3        | lev4  |
+-------------+----------------------+-------------+-------+
| ELECTRONICS | PORTABLE ELECTRONICS | MP3 PLAYERS | FLASH |
+-------------+----------------------+-------------+-------+
1 row in set (0.01 sec)

이러한 접근 방식의 주된 한계는 계층 구조의 모든 수준에 대해 하나의 자체 조인이 필요하며 조인이 복잡해지면서 각 수준이 추가 될 때마다 성능이 자연스럽게 저하된다는 것입니다.



부작용 목록 모델의 제한(LIMITATIONS OF THE ADJACENCY LIST MODEL)


순수 SQL에서 인접성 목록 모델로 작업하는 것은 기껏해야 어려울 수 있습니다. 카테고리의 전체 경로를 표시되기 전에, 우리는 존재하는 레벨을 알 수있다. 그 과정에서 전체 서브 트리를 orphaning에 대한 때문에 가능성의 노드를 삭제하는 경우 또한, 특별한주의는 (휴대용 전자 제품 카테고리와 분리되어 자식을 모두 삭제)주의해야합니다. 이러한 제한 중 일부는 클라이언트 측 코드 나 저장 프로 시저를 사용하여 해결할 수 있습니다. 절차 언어로 우리는 나무의 아래에서 시작하여 전체 트리 또는 단일 경로를 반환 위쪽으로 반복 할 수 있습니다. 프로 시저 프로그래밍을 사용하여 하나의 자식 요소를 승격시키고 나머지 자식을 새로운 부모를 가리 키도록 다시 정렬함으로써 전체 하위 트리를 고아없이 노드를 삭제할 수도 있습니다.



중첩 세트 모델(The Nested Set Model)


이 기사에서 중점을두고 자하는 것은 일반적으로 중첩 세트 모델이라고하는 다른 접근 방식입니다. 중첩 세트 모델에서는 노드와 라인이 아닌 중첩 된 컨테이너로 새로운 방식으로 계층 구조를 볼 수 있습니다. 다음과 같이 전자 제품 범주를 묘사 해보십시오.



부모 범주가 자녀를 감싸고 있기 때문에 우리 계층 구조가 어떻게 유지되고 있는지 주목하십시오. 우리는 노드의 중첩을 나타 내기 위해 왼쪽 및 오른쪽 값을 사용하여 테이블에서이 계층 구조를 나타냅니다.


CREATE TABLE nested_category (
        category_id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(20) NOT NULL,
        lft INT NOT NULL,
        rgt INT NOT NULL
);

INSERT INTO nested_category VALUES(1,'ELECTRONICS',1,20),(2,'TELEVISIONS',2,9),(3,'TUBE',3,4),
 (4,'LCD',5,6),(5,'PLASMA',7,8),(6,'PORTABLE ELECTRONICS',10,19),(7,'MP3 PLAYERS',11,14),(8,'FLASH',12,13),
 (9,'CD PLAYERS',15,16),(10,'2 WAY RADIOS',17,18);

SELECT * FROM nested_category ORDER BY category_id;

+-------------+----------------------+-----+-----+
| category_id | name                 | lft | rgt |
+-------------+----------------------+-----+-----+
|           1 | ELECTRONICS          |   1 |  20 |
|           2 | TELEVISIONS          |   2 |   9 |
|           3 | TUBE                 |   3 |   4 |
|           4 | LCD                  |   5 |   6 |
|           5 | PLASMA               |   7 |   8 |
|           6 | PORTABLE ELECTRONICS |  10 |  19 |
|           7 | MP3 PLAYERS          |  11 |  14 |
|           8 | FLASH                |  12 |  13 |
|           9 | CD PLAYERS           |  15 |  16 |
|          10 | 2 WAY RADIOS         |  17 |  18 |
+-------------+----------------------+-----+-----+

왼쪽과 오른쪽은 MySQL에서 예약어이기 때문에 lft와 rgt를 사용합니다. 예약어 전체 목록은 http://dev.mysql.com/doc/mysql/en/reserved-words.html을 참조하십시오.


그렇다면 우리는 어떻게 좌우 값을 결정합니까? 바깥 쪽 노드의 가장 왼쪽에 번호를 매기고 오른쪽으로 계속합니다.





이 디자인은 일반적인 트리에도 적용 할 수 있습니다.



트리를 사용하여 작업 할 때 우리는 왼쪽에서 오른쪽으로 한 번에 한 레이어 씩 작업하고 각 노드의 하위 노드로 내림차순 번호를 할당하고 오른쪽으로 이동합니다. 이 접근법을 수정 된 선주문 트리 순회 알고리즘이라고합니다.




풀 트리 검색하기(RETRIEVING A FULL TREE)


우리는 노드의 lft 값이 부모의 lft와 rgt 값 사이에 항상 존재한다는 것을 기반으로 부모를 노드와 연결하는 자체 조인을 사용하여 전체 트리를 검색 할 수 있습니다.


SELECT node.name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND parent.name = 'ELECTRONICS'
ORDER BY node.lft;

+----------------------+
| name                 |
+----------------------+
| ELECTRONICS          |
| TELEVISIONS          |
| TUBE                 |
| LCD                  |
| PLASMA               |
| PORTABLE ELECTRONICS |
| MP3 PLAYERS          |
| FLASH                |
| CD PLAYERS           |
| 2 WAY RADIOS         |
+----------------------+


인접성 목록 모델을 사용한 이전 예제와 달리이 쿼리는 트리의 깊이에 관계없이 작동합니다. rgt 값은 항상 lft 값과 같은 부모 내에 속하기 때문에 우리는 BETWEEN 절의 노드의 rgt 값에 신경 쓰지 않습니다.




모든 잎사귀 찾기(FINDING ALL THE LEAF NODES)


인접 목록 모델에서 사용 된 LEFT JOIN 방법보다 훨씬 단순한 중첩 된 집합 모델의 모든 리프 노드 찾기. nested_category 테이블을 보면 리프 노드의 lft 및 rgt 값이 연속 된 숫자임을 알 수 있습니다. 리프 노드를 찾기 위해 rgt = lft + 1 인 노드를 찾는다.


SELECT name
FROM nested_category
WHERE rgt = lft + 1;

+--------------+
| name         |
+--------------+
| TUBE         |
| LCD          |
| PLASMA       |
| FLASH        |
| CD PLAYERS   |
| 2 WAY RADIOS |
+--------------+



단일 경로 검색(RETRIEVING A SINGLE PATH)


중첩 된 집합 모델을 사용하면 여러자가 조인을 사용하지 않고도 단일 경로를 검색 할 수 있습니다.


SELECT parent.name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.name = 'FLASH'
ORDER BY parent.lft;

+----------------------+
| name                 |
+----------------------+
| ELECTRONICS          |
| PORTABLE ELECTRONICS |
| MP3 PLAYERS          |
| FLASH                |
+----------------------+


NODES의 깊이 찾기(FINDING THE DEPTH OF THE NODES)


우리는 이미 전체 트리를 표시하는 방법을 살펴 보았지만 트리의 각 노드의 깊이를 표시하여 각 노드가 계층 구조에 어떻게 들어 맞는지 더 잘 식별하려는 경우 어떻게해야할까요? 이것은 전체 트리를 표시하기 위해 COUNT 함수와 GROUP BY 절을 기존 쿼리에 추가하여 수행 할 수 있습니다.


SELECT node.name, (COUNT(parent.name) - 1) AS depth
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+----------------------+-------+
| name                 | depth |
+----------------------+-------+
| ELECTRONICS          |     0 |
| TELEVISIONS          |     1 |
| TUBE                 |     2 |
| LCD                  |     2 |
| PLASMA               |     2 |
| PORTABLE ELECTRONICS |     1 |
| MP3 PLAYERS          |     2 |
| FLASH                |     3 |
| CD PLAYERS           |     2 |
| 2 WAY RADIOS         |     2 |
+----------------------+-------+

depth 값을 사용하여 범주 이름을 CONCAT 및 REPEAT 문자열 함수로 들여 쓸 수 있습니다.


SELECT CONCAT( REPEAT(' ', COUNT(parent.name) - 1), node.name) AS name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  PORTABLE ELECTRONICS |
|   MP3 PLAYERS         |
|    FLASH              |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
+-----------------------+

물론 클라이언트 측 응용 프로그램에서는 깊이 값을 직접 사용하여 계층 구조를 표시 할 가능성이 높습니다. 웹 개발자는 깊이 번호가 증가하거나 감소 할 때 <li> </ li> 및 <ul> </ ul> 태그를 추가하여 트리를 반복 할 수 있습니다.



서브 트리의 깊이(DEPTH OF A SUB-TREE)


하위 트리에 대한 깊이 정보가 필요하면 결과를 손상시킬 수 있으므로 자체 조인에서 노드 또는 부모 테이블을 제한 할 수 없습니다. 대신 세 번째 자체 조인을 하위 쿼리와 함께 추가하여 하위 트리의 새로운 시작점이 될 깊이를 결정합니다.


SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
        nested_category AS parent,
        nested_category AS sub_parent,
        (
                SELECT node.name, (COUNT(parent.name) - 1) AS depth
                FROM nested_category AS node,
                nested_category AS parent
                WHERE node.lft BETWEEN parent.lft AND parent.rgt
                AND node.name = 'PORTABLE ELECTRONICS'
                GROUP BY node.name
                ORDER BY node.lft
        )AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
        AND sub_parent.name = sub_tree.name
GROUP BY node.name
ORDER BY node.lft;

+----------------------+-------+
| name                 | depth |
+----------------------+-------+
| PORTABLE ELECTRONICS |     0 |
| MP3 PLAYERS          |     1 |
| FLASH                |     2 |
| CD PLAYERS           |     1 |
| 2 WAY RADIOS         |     1 |
+----------------------+-------+

이 기능은 루트 노드를 포함한 모든 노드 이름과 함께 사용할 수 있습니다. 깊이 값은 항상 명명 된 노드를 기준으로합니다.



NODE의 즉각적인 소굴 찾기(FIND THE IMMEDIATE SUBORDINATES OF A NODE)


소매업 자 웹 사이트에서 전자 제품 범주를 보여주고 있다고 가정 해보십시오. 사용자가 카테고리를 클릭하면 해당 카테고리의 제품을 표시하고 그 바로 아래의 하위 카테고리는 나열하지만 하위 카테고리의 전체 트리는 나열하지 않을 수 있습니다. 이를 위해 우리는 노드와 노드의 즉각적인 하위 노드를 보여줄 필요가 있지만 트리를 더 내려 가지 않아야합니다. 예를 들어 휴대용 전자 제품 카테고리를 표시 할 때 MP3 플레이어, CD 플레이어 및 2 가지 라디오를 표시하려고하지만 플래시는 표시하지 않을 것입니다.


이는 이전 쿼리에 HAVING 절을 추가하여 쉽게 수행 할 수 있습니다.


SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
        nested_category AS parent,
        nested_category AS sub_parent,
        (
                SELECT node.name, (COUNT(parent.name) - 1) AS depth
                FROM nested_category AS node,
                        nested_category AS parent
                WHERE node.lft BETWEEN parent.lft AND parent.rgt
                        AND node.name = 'PORTABLE ELECTRONICS'
                GROUP BY node.name
                ORDER BY node.lft
        )AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
        AND sub_parent.name = sub_tree.name
GROUP BY node.name
HAVING depth <= 1
ORDER BY node.lft;

+----------------------+-------+
| name                 | depth |
+----------------------+-------+
| PORTABLE ELECTRONICS |     0 |
| MP3 PLAYERS          |     1 |
| CD PLAYERS           |     1 |
| 2 WAY RADIOS         |     1 |
+----------------------+-------+


상위 노드를 표시하지 않으려면, HAVING depth <= 1 행을 HAVING depth = 1로 변경하십시오.



중첩 된 기능의 전체 기능(AGGREGATE FUNCTIONS IN A NESTED SET)


집계 함수를 설명하는 데 사용할 수있는 제품 표를 추가해 보겠습니다.


CREATE TABLE product
(
        product_id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(40),
        category_id INT NOT NULL
);

INSERT INTO product(name, category_id) VALUES('20" TV',3),('36" TV',3),
('Super-LCD 42"',4),('Ultra-Plasma 62"',5),('Value Plasma 38"',5),
('Power-MP3 5gb',7),('Super-Player 1gb',8),('Porta CD',9),('CD To go!',9),
('Family Talk 360',10);

SELECT * FROM product;

+------------+-------------------+-------------+
| product_id | name              | category_id |
+------------+-------------------+-------------+
|          1 | 20" TV            |           3 |
|          2 | 36" TV            |           3 |
|          3 | Super-LCD 42"     |           4 |
|          4 | Ultra-Plasma 62"  |           5 |
|          5 | Value Plasma 38"  |           5 |
|          6 | Power-MP3 128mb   |           7 |
|          7 | Super-Shuffle 1gb |           8 |
|          8 | Porta CD          |           9 |
|          9 | CD To go!         |           9 |
|         10 | Family Talk 360   |          10 |
+------------+-------------------+-------------+



이제 각 카테고리의 제품 수와 함께 카테고리 트리를 검색 할 수있는 쿼리를 생성 해 보겠습니다.


SELECT parent.name, COUNT(product.name)
FROM nested_category AS node ,
        nested_category AS parent,
        product
WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.category_id = product.category_id
GROUP BY parent.name
ORDER BY node.lft;

+----------------------+---------------------+
| name                 | COUNT(product.name) |
+----------------------+---------------------+
| ELECTRONICS          |                  10 |
| TELEVISIONS          |                   5 |
| TUBE                 |                   2 |
| LCD                  |                   1 |
| PLASMA               |                   2 |
| PORTABLE ELECTRONICS |                   5 |
| MP3 PLAYERS          |                   2 |
| FLASH                |                   1 |
| CD PLAYERS           |                   2 |
| 2 WAY RADIOS         |                   1 |
+----------------------+---------------------+


이것은 COUNT 및 GROUP BY가 추가 된 일반적인 트리 전체 쿼리와 WHERE 절의 노드와 product 테이블 간의 조인 및 product 테이블에 대한 참조입니다. 보시다시피 각 카테고리에 대한 개수가 있으며 하위 카테고리의 수는 상위 카테고리에 반영됩니다.



새로운 노드 추가하기(ADDING NEW NODES)


이제 트리를 쿼리하는 방법을 배웠으므로 새 노드를 추가하여 트리를 업데이트하는 방법을 살펴보아야합니다. 중첩 된 집합 다이어그램을 다시 살펴 보겠습니다.




TELEVISIONS 노드와 PORTABLE ELECTRONICS 노드 사이에 새로운 노드를 추가하고자한다면, 새로운 노드는 lft와 rgt 값이 10과 11이 될 것이고, 오른쪽 노드는 lft와 rgt 값이 2만큼 증가 할 것입니다. 그런 다음 적절한 lft 및 rgt 값을 가진 새 노드를 추가합니다. 이 작업은 MySQL 5의 저장 프로 시저에서 수행 할 수 있지만 가장 안정적인 버전이므로 대부분의 독자가 4.1을 사용하고 있다고 가정하고 대신 LOCK TABLES 문으로 쿼리를 분리합니다.


LOCK TABLE nested_category WRITE;

SELECT @myRight := rgt FROM nested_category
WHERE name = 'TELEVISIONS';

UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft + 2 WHERE lft > @myRight;

INSERT INTO nested_category(name, lft, rgt) VALUES('GAME CONSOLES', @myRight + 1, @myRight + 2);

UNLOCK TABLES;

우리는 들여 쓰기 트리 쿼리를 사용하여 중첩을 확인할 수 있습니다.


SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  GAME CONSOLES        |
|  PORTABLE ELECTRONICS |
|   MP3 PLAYERS         |
|    FLASH              |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
+-----------------------+



기존의 자식이없는 노드의 자식으로 노드를 추가하려면이 절차를 약간 수정해야합니다. 2 WAY RADIOS 노드 아래에 새 FRS 노드를 추가해 보겠습니다.


LOCK TABLE nested_category WRITE;

SELECT @myLeft := lft FROM nested_category

WHERE name = '2 WAY RADIOS';

UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > @myLeft;
UPDATE nested_category SET lft = lft + 2 WHERE lft > @myLeft;

INSERT INTO nested_category(name, lft, rgt) VALUES('FRS', @myLeft + 1, @myLeft + 2);

UNLOCK TABLES;

이 예제에서 우리는 자랑스러운 새로운 부모 노드의 왼쪽 숫자의 오른쪽으로 모든 것을 확장 한 다음 노드를 왼쪽 값의 오른쪽에 배치합니다. 보시다시피 새 노드가 제대로 중첩되었습니다.


SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  GAME CONSOLES        |
|  PORTABLE ELECTRONICS |
|   MP3 PLAYERS         |
|    FLASH              |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
|    FRS                |
+-----------------------+


노드 삭제(DELETING NODES)


중첩 된 세트를 사용하여 작업하는 마지막 기본 작업은 노드를 제거하는 것입니다. 노드를 삭제할 때 수행하는 과정은 계층 구조에서 노드의 위치에 따라 달라집니다. 분리 된 노드를 처리해야하기 때문에 리프 노드를 삭제하는 것이 자식 노드를 삭제하는 것보다 쉽습니다.


잎 노드를 삭제할 때, 새로운 노드를 추가하는 것과 정반대의 과정을 거치면 모든 노드에서 오른쪽으로 노드와 노드의 너비가 삭제됩니다.


LOCK TABLE nested_category WRITE;

SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'GAME CONSOLES';

DELETE FROM nested_category WHERE lft BETWEEN @myLeft AND @myRight;

UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft - @myWidth WHERE lft > @myRight;

UNLOCK TABLES;

그리고 다시 한번 들여 쓰기 트리 쿼리를 실행하여 노드가 계층 구조를 손상시키지 않고 삭제되었음을 확인합니다.


SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  PORTABLE ELECTRONICS |
|   MP3 PLAYERS         |
|    FLASH              |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
|    FRS                |
+-----------------------+


이 접근법은 노드와 그 모든 자식 노드를 삭제하는 것과 똑같이 잘 작동합니다.


LOCK TABLE nested_category WRITE;

SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'MP3 PLAYERS';

DELETE FROM nested_category WHERE lft BETWEEN @myLeft AND @myRight;

UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft - @myWidth WHERE lft > @myRight;

UNLOCK TABLES;

다시 한번 전체 하위 트리를 성공적으로 삭제했는지 쿼리합니다.


SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+-----------------------+
| name                  |
+-----------------------+
| ELECTRONICS           |
|  TELEVISIONS          |
|   TUBE                |
|   LCD                 |
|   PLASMA              |
|  PORTABLE ELECTRONICS |
|   CD PLAYERS          |
|   2 WAY RADIOS        |
|    FRS                |
+-----------------------+

우리가 처리해야하는 또 다른 시나리오는 부모 노드는 삭제하지만 자식 노드는 삭제하지 않는 것입니다. 어떤 경우에는 감독자가 해고 될 때와 같이 대체가 표시 될 때까지 이름을 자리 표시 자로 바꿀 수도 있습니다. 다른 경우, 자식 노드는 모두 삭제 된 부모 수준까지 이동해야합니다.


LOCK TABLE nested_category WRITE;

SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1
FROM nested_category
WHERE name = 'PORTABLE ELECTRONICS';

DELETE FROM nested_category WHERE lft = @myLeft;

UPDATE nested_category SET rgt = rgt - 1, lft = lft - 1 WHERE lft BETWEEN @myLeft AND @myRight;
UPDATE nested_category SET rgt = rgt - 2 WHERE rgt > @myRight;
UPDATE nested_category SET lft = lft - 2 WHERE lft > @myRight;

UNLOCK TABLES;

이 경우 노드의 오른쪽에있는 모든 요소에서 2를 뺍니다 (자식이 없으므로 너비가 2가되기 때문에). 자식 노드가 하나 인 노드에서 하나를 뺍니다 (부모의 왼쪽 손실로 인해 생성 된 간격을 닫습니다. 값). 다시 한번, 우리는 요소가 홍보되었음을 확인할 수 있습니다.


SELECT CONCAT( REPEAT( ' ', (COUNT(parent.name) - 1) ), node.name) AS name
FROM nested_category AS node,
        nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
ORDER BY node.lft;

+---------------+
| name          |
+---------------+
| ELECTRONICS   |
|  TELEVISIONS  |
|   TUBE        |
|   LCD         |
|   PLASMA      |
|  CD PLAYERS   |
|  2 WAY RADIOS |
|   FRS         |
+---------------+

노드를 삭제할 때의 다른 시나리오는 자식 노드 중 하나를 부모 위치로 승격시키고 부모 노드의 형제 노드 아래에 자식 노드를 이동시키는 것입니다. 그러나 공간을 위해이 시나리오는이 기사에서 다루지 않습니다.




최종 생각(Final Thoughts)


이 기사의 정보가 사용자에게 유용하기를 희망하지만 SQL에서 중첩 된 집합의 개념은 10 년 이상되어 왔으며 책과 인터넷에서 사용할 수있는 많은 추가 정보가 있습니다. 제 생각에 계층 적 정보를 관리하는 데있어 가장 포괄적 인 정보원은 고급 SQL, Joe Celko 분야에서 매우 존경받는 저자가 작성한 Joe Celko의 "Smarties 용 SQL의 Trees and Hierarchies"입니다. 조 셀코 (Joe Celko)는 중첩 된 세트 모델로 종종 공로를 인정 받았으며이 주제에 관한 가장 많은 저술가입니다. Celko의 저서가 저의 연구에서 귀중한 자원이라는 것을 알았습니다. 이 기사에서는이 기사에서 다루지 않은 고급 항목에 대해 설명하고 인접 목록 및 중첩 세트 모델 외에도 계층 적 데이터를 관리하기위한 추가 방법을 제공합니다.


참고 자료 / 참고 자료 섹션에서 나는 MySQL에서 중첩 된 세트를 처리하기 위해 미리 빌드 된 PHP 라이브러리를 포함하는 한 쌍의 PHP 관련 리소스를 포함하여 계층 적 데이터 관리 연구에 사용할 수있는 웹 리소스를 나열했다. 현재 인접 목록 모델을 사용 중이며 중첩 된 세트 모델을 실험하고 싶은 사용자는 아래에 나열된 데이터베이스 리소스의 계층 적 데이터 저장에서 둘 사이의 변환을위한 샘플 코드를 찾을 수 있습니다.







Comment +0

JAVA에서 이미지를 그리기 위한 속도는 다른 언어보다 많이 느립니다.

아래는 몇가지 이미지 작업을 하면서 참고가 될만한 팁을 소개합니다.


1. 메모리에서 변환을 수행해도 작업 속도가 향상되지 않습니다.

2. ImageIO.write 이미지에 BufferedOutputStream을 사용하면 일부 시스템에서 약간의 개선이있는 것으로 보입니다.

3. ImageIO.write는 Indexed Colorspaces에 최적화되어 있으므로 이미지를 Index Colorspace로 변환 한 다음 쓸 때가 더 빠릅니다. 

다음은 몇 가지 샘플 코드입니다.


BufferedImage indexedImage = new BufferedImage(image.getWidth(),
image.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
Graphics2D g = indexedImage.createGraphics();
g.drawImage(image, 0,0,null);
image=indexedImage;


4. JAI JPEG 디코딩은 ImageIO보다 훨씬 느립니다.

5. ImageIO.setUseCache (true)가 더 느립니다.

Comment +0

<mvc:annotation-driven>은 @MVC 스타일의 컨트롤러에서 자주 사용되는 validator, conversionService, messageConverter를 자동으로 등록해주는 간편 빈 설정용 태그이다.

이 태그에 의해서 자동으로 등록되는 기능들에 대해서는 3.0.1의 보강된 레퍼런스 문서에 다음과 같이 잘 나와있다.

  1. Support for Spring 3′s Type ConversionService in addition to JavaBeans PropertyEditors during Data Binding. A ConversionService instance produced by the org.springframework.format.support.FormattingConversionServiceFactoryBean is used by default. This can be overriden by setting the conversion-service attribute.
  2. Support for formatting Number fields using the @NumberFormat annotation
  3. Support for formatting Date, Calendar, Long, and Joda Time fields using the @DateTimeFormat annotation, if Joda Time is present on the classpath.
  4. Support for validating @Controller inputs with @Valid, if a JSR-303 Provider is present on the classpath. The validation system can be explicitly configured by setting the validator attribute.
  5. Support for reading and writing XML, if JAXB is present on the classpath.
  6. Support for reading and writing JSON, if Jackson is present on the classpath.

그런데 과연 이게 전부일까?

아니다.

<mvc:annotation-driven>이 등록해주는 빈이 한가지 더 있다. 바로 MappedInterceptor이다. 정확히 말하자면 모든 경로에 대해서 적용되는 ConversionServiceExposingInterceptor를 가진 MappedInterceptor이다.

아직 많은 개발자들에게는 MappedInterceptor도 3.0에서 새로 추가된 것이라 생소할 것이다. 기존 HandlerInterceptor는 핸들러 매핑의 프로퍼티로 등록된다. 이 때문에 두 가지 단점이 있는데, 첫 째는 핸들러 매핑 전략을 두 개 이상을 병행해서 사용하면 인터셉터를 일일히 핸들러 매핑마다 등록해줘야 한다는 점이다. 둘 째는 핸들러 매핑에 등록한 인터셉터는 그 핸들러 매핑이 처리하는 모든 요청에 적용된다는 점이다. URL에 따라 적용 인터셉터를 선별하려면 인터셉터 안에서 URL을 필터링 하는 작업을 해야 한다.

MappedInterceptor는 이 두가지 단점을 보완해주는 새로운 인터셉터 적용방식이다. MappedInterceptor를 빈으로 등록하고 path와 interceptor를 지정해주면 해당 경로패턴에 일치하는 URL에 대해서는 어떤 핸들러 매핑을 이용하든 상관없이 지정한 인터셉터를 적용해준다. 일종의 글로벌 스마트 인터셉터 레지스트리라고나 할까. 이 MappedInterceptor의 등장으로 앞으로는 핸들러 매핑에 인터셉터를 등록해서 사용할 일은 없을 듯 하다.

아무튼, <mvc:annotation-driven>는 뜬금없이 이 MappedInterceptor를 등록해버린다. 그것도 모든 경로에 대해서 적용한다. 추가된 인터셉터는 ConversionServiceExposingInterceptor라는 단순한 것인데, ConversionService 오브젝트를 request의 애트리뷰트에 등록해주는 간단한 작업을 수행한다. ConversionService라면 AnnotationMethodHandlerAdapter에 의해서 @ModelAttribute에 바인딩할 때 사용할 오브젝트이다. <mvc:annotation-driven>에서는 conversion-service 애트리뷰트에 의해서 재구성한 ConversionService를 지정할 수 있다.

<mvc:annotation-driven>는 바로 이 ConversionService를 request 스코프 애트리뷰트에 등록한다. 도데체 어디서 써먹으려는 것일까?

 

용도는 바로 <spring:eval>이다. 새롭게 추가된 <spring:eval>은 JSP에서 JSP EL을 대신해서 초강력 SpEL을 사용할 수 있도록 추가된 태그이다. 바로 이 <spring:eval>이 모델 오브젝트의 프로퍼티 값을 텍스트로 변환할 때 ConversionServiceExposingInterceptor에 의해서 등록된 ConversionService가 적용되는 것이다.

<mvc:annotation-driven>을 사용하지 않아서 ConversionService가 등록되지 않았다면 <spring:eval>은 기본적인 타입변환만 지원하는 StandardTypeConverter을 사용한다. 그보다는 ConversionServiceExposingInterceptor을 통해서 등록되는 FormattingConversionService가 훨씬 강력할 것이다. 모델에 @NumberFormat이나 @DateTimeFormat같은 포맷터가 달려있다면 <spring:eval>만으로도 이 포맷팅이 적용된다는 뜻이다.

기존에는 PropertyEditor가 적용되는 폼에서나 포맷을 가진 프로퍼티 바인딩을 적용할 수 있었다. 단순 출력을 필요로 하는 곳에서는 JSP EL을 사용해야 했기에 모델 오브젝트 프로퍼티의 포맷을 지정하려면 지저분한 <fmt:formatNumber> 따위의 태그를 사용해야만 했다. 그것도 몇가지 종류의 포맷밖에 지원이 안된다.

하지만 이제 3.0.1부터는 <spring:eval>을 사용해서 ConversionService의 막강한 타입변환-포맷팅 기능을 적용할 수 있다.

 

아직 레퍼런스나 API문서 등에도 공개되지 않은 내용인데.. 이런 중요한 정보를 미리미리 알려주지 않다니!

3.0.1의 막강한 @MVC와 ConversionService, Formatter, JSR-303 Bean Validation, Message Converter, AJAX 등등등을 활용한 편리한 웹 개발에 대해서 자세히 알고 싶다면 조만간 출시 예정인 스프링 3.0 책을 참고하도록… 



출처 : http://toby.epril.com/?p=989

Comment +0

hsqldb 서버 실행

java -cp hsqldb-2.2.9.jar org.hsqldb.server.Server --database.0 file:/User/test/hsqldb/testDb --dbname.0 testDb --remote_open true


데이터베이스 메니저 실행

java -cp hsqldb-2.2.9.jar org.hsqldb.util.DatabaseManager

'DEVELOPMENT' 카테고리의 다른 글

IDE 단축키  (0) 2018.11.28
로컬 jar 파일 maven porject에 추가하는 법  (0) 2017.04.25
hsqldb 사용하기  (0) 2016.07.23
windows에 있는 AppData 폴더란?  (0) 2015.07.30
Git  (0) 2013.12.30
dxf file format  (0) 2012.08.16

Comment +0