Metaworks Database/UI Generation Framework


개요
database 처리에 있어 JDBC와 SQL문을 직접 이용할 때 발생하는 구현에 치중한 코딩, 반복적인 작업(컬럼명 맞추기, type validation, 문자열 처리등)그리고 그로 인해 발생하는 여러가지 에러처리, 디버깅작업, 유지보수 작업 등 여러가지 오버헤드가 있습니다. 그래서 이런 오버헤드를 피하기 위해 SQL문 대신 추상화된 Class를 이용하여 관념적인 프로그래밍 방법으로 DB 처리를 대신하기 위한 Framework API 입니다.
Packages
org.metaworks  
org.metaworks.inputter  
org.metaworks.query  
org.metaworks.ui  
org.metaworks.validator  
org.metaworks.viewer  
org.metaworks.web  
org.metaworks.wizard  

 


서비스

이 Framework은 다음의 서비스를 지원합니다. 한 마디로 SQL문장으로 처리하던 모든 것을 JAVA 자료구조와 문법구조에 에뮬레이션한다고 생각하면 쉽게 이해하실 수 있습니다. 다음 표는 그러한 맵핑 구조를 보여줍니다.

※ Metaworks Class and Database Mapping
class 명
Database Mapping
주요 메서드
Database Mapping
Table Table load, find select
Record Record (tuple) save, update, delete insert, update, delete
Field Data get, put select <fieldname>, update set <field>
FieldDescriptor Field Declaration get, put desc <table>
FieldAttribute Field 속성 (size, default...)
Inputter 입력방법
Validator Check Constraint isValid check (...)

프로그래머 가이드
 
  1. Table 정의
  2. 	Class.forName("oracle.jdbc.driver.OracleDriver");
    	Connection con = DriverManager.getConnection("jdbc:oracle:thin:@165.186.52.29:1526:iman5", "infodba", "ckddnjs5");
    
    	// table을 생성한다. db의 'create'문을 연상
    	Table px_part_newparts = new Table(
    		"px_part_newparts",     //  table명
    					//  column              title           type            iskey
    		new FieldDescriptor[]{
    			new FieldDescriptor("SEQNO",            "순번",         Types.INTEGER,  true),
    			new FieldDescriptor("DESCRIPTION",      "설명"),
    			new FieldDescriptor("DEVELOPMENTDATE",  "개발일정",     Types.DATE),
    			new FieldDescriptor("DIVISION",         "사업부")
    		},
    		con			// 이 table이 사용할 db connection
    	);
    
    
  3. Record 처리 - 검색, 저장, 수정, 삭제
  4. 검색

    	// 위에서 정의한 table에서 seqno=10000인 레코드를 검색
    	Record [] rec = px_part_newparts.find("SEQNO=10000");
         
         	// 하나라도 찾았으면 그 값을 화면에 뿌림           
    	if(rec.length> 0){
    		System.out.println("DESCRIPTION = " + rec[0].get("DESCRIPTION"));
    		System.out.println("SEQNO = "+rec[0].get("SEQNO"));
    	}
    
    수정
    	rec[0].put("DESCRIPTION", "바꾼내용");
    	rec[0].update();
    
    삭제
    	rec[0].delete();
    
    새 레코드 생성해서 insert(save)하기
    	Record newRec = new Record(px_part_newparts);
    	
    	newRec.put("SEQNO", "5555");
    	newRec.put("DESCRIPTION", "새 레코드의 description");
    	newRec.put("DIVISION", "CNZ");
    	
    	newRec.save();
    
  5. 자동 UI 생성 - InputForm 활용
  6. 주어진 Table의 한 Row를 입력받을 수 있는 Form Panel (Swing JPanel)을 생성한다.

    Example 1) 폼 생성

            public static void main(String args[]) throws Exception{
    
    
                    Class.forName("oracle.jdbc.driver.OracleDriver");
                                                                                                                                                                                                    // ~/orawin95/network/admin/tnsnames.ora file 참조.
                    Connection con = DriverManager.getConnection("jdbc:oracle:thin:@165.186.52.29:1526:iman5", "infodba", "ckddnjs5");
                    
                    // table을 생성한다. db의 'create'문을 연상
                    Table px_part_newparts = new Table(
                            "px_part_newparts",     //  table명
                                                    //  column              title           type            iskey
                            new FieldDescriptor[]{
                                    new FieldDescriptor("SEQNO",            "순번",         Types.INTEGER,  true),
                                    new FieldDescriptor("description",      "설명"),
                                    new FieldDescriptor("developmentdate",  "개발일정",     Types.DATE),
                                    new FieldDescriptor("DIVISION",         "사업부")
                            },
                            con
                    );
                    
                    Record [] rec = px_part_newparts.find("seqno=10000");
                    
                    if(rec.length> 0){
                            System.out.println("description = " + rec[0].get("description"));
                            System.out.println("seqno = "+rec[0].get("seqno"));
                            
                    }                       
                            
                    final InputForm newForm = new InputForm(px_part_newparts, "10000");
                    
                    JFrame frame = new JFrame("test");
                    
                    frame.getContentPane().setLayout(new BorderLayout());
                    frame.getContentPane().add("Center", newForm);  // ◀◀◀◀◀◀
                    
                    JButton saveBtn = new JButton("저장");
                    frame.getContentPane().add("South", saveBtn);   // ◀◀◀◀◀◀
    
                    saveBtn.addActionListener(new ActionListener(){
                            public void actionPerformed(ActionEvent e){
                                    try{
                                            newForm.getRecord().save();
                                    }catch(Exception ex){}
                            }
                    });
                    
    
                    frame.pack();
                    frame.setVisible(true);
            }       
     
    Example 2) 다이얼 로그를 만들어 입력 폼 띄우기

                    InputForm testForm2 = new InputForm(
                            new Table("test2",
                                    new FieldDescriptor[]{
                                            new FieldDescriptor("column1"),
                                            new FieldDescriptor("column2"),
                                            new FieldDescriptor("column3")
                                    }
                            )
                    ){
                            public void onSaveOK(Record rec, JDialog dialog){
                                    com.ugsolutions.util.MessageBox.post("'"+ rec.get("column1") +"' 레코드가 저장되었습니다.", "저장완료", 1);
                            }
                            
                            public void onSaveFailed(Exception e, JDialog dialog){
                                    com.ugsolutions.util.MessageBox.post("저장 실패 했습니다.\n 에러=" + e.getMessage(), "실패", 1);
                            }
                    };
                    testForm2.postInputDialog(frame);
    
     
  7. 일관화된 입력방법 - Inputter
  8. Swing 입력 UI Component들을 일관적인 방법으로 처리하기 위한 Interface로 재활용성과 추상화가 쉬워 UI개발 및 확장이 유연하고 용이합니다.

    기본으로 제공되는 Inputter들(com.cwpdm.util.db.inputter.*)
    Inputter class명 설명 default type(세팅하지 않아도 자동으로 선택되는 Type
    TextInput JTextField 입력창 Types.VARCHAR
    NumberInput 숫자만 입력되고 right 배치인 JTextField Types.INTEGER
    DateInput 달력 DateButton Types.DATE
    SelectInput JComboBox로 되고 옵션중에 하나만 선택 없슴
    RadioInput RadioButton로 되고 옵션중에 하나만 선택 없슴
    ReferenceInput 선택 PopupButton으로 되고 db에서 옵션을 읽어와 하나만 선택 없슴

    사용예제
    FieldDescriptor의 6번째 parameter에 인자로 넣으면 된다.

    	new FieldDescriptor("COMMON",		"공용여부",	12,	false,		
    		new FieldAttribute[]{
    			new FieldAttribute("default", new String("개조")),
    		},
    		new SelectInput(new String[]{"", "신작", "개조", "수량추가"})	//◀◀◀◀◀
    	),
    	
    	new FieldDescriptor("TARGETPRICE",	"목표단가",	2,	false,
    		null,
    		new NumberInput()  //◀◀◀◀◀
    	),
    	
    	...
    
    위와 같이 table을 정의한 후에 InputForm class를 이용해 Form panel을 만듭니다.
    	InputForm inputForm = new InputForm(table);
    	someFrame.add(inputForm);
    	someFrame.setVisible(true);
    	...
    
    나만의 Inputter 만들기
    com.cwpdm.util.db.inputter.AbstractComponentInputter를 상속하여 새 Inputter를 생성할 수 있습니다.
    	public class AbstractComponentInputter{
    	
    		// InputForm이 값을 얻어낼 때 사용할 로직
    		abstract public Object getValue();
    			
    		// InputForm이 값을 세팅해서 보여줄 때(수정 폼/초기치 세팅시) 사용할 로직
    		abstract public void setValue(Object obj);
    		
    		// InputForm이 새로 컴포넌트를 얻어야 할 때 사용할 로직
    		abstract public Component getNewComponent();
    	}
    
    아래의 예제는 DateInput의 소스코드입니다. (inputter.*를 보시면 다양한 예제를 볼 수 있습니다)
    	package com.cwpdm.util.db.inputter;
    	
    	import java.awt.Component;
    	import javax.swing.*;
    	
    	import com.ugsolutions.util.*;
    	import java.util.Date;
    	import java.text.*;
    	
    	
    	public class DateInput extends AbstractComponentInputter{
    	
    		public DateInput(){
    			super();
    		}
    		
    	/////////// implemetations //////////////
    	
    		// InputForm이 값을 얻어낼 때 사용할 로직
    		public Object getValue(){
    			Date srcDate = ((DateButton)getComponent()).getDate();
    			
    			if(srcDate==null) return null;
    			
    			Date rtnDate = new Date(srcDate.getTime()){
    				public String toString(){
    				       	SimpleDateFormat displayFormatter = new SimpleDateFormat("yyyy-MM-dd");
    					String making = displayFormatter.format(this);
    							
    					return making;
    				}			
    			};
    			
    			return rtnDate;
    	
    		}
    		
    		// InputForm이 값을 세팅해서 보여줄 때(수정 폼/초기치 세팅시) 사용할 로직
    		public void setValue(Object obj){
    			if(obj instanceof Date && obj!=null)
    				((DateButton)getComponent()).setDate((Date)obj);
    		}
    
    		// InputForm이 새로 컴포넌트를 얻어야 할 때 사용할 로직
    		public Component getNewComponent(){
    			DateButton dateButton = new DateButton();
    			dateButton.setDisplayFormat("yyyy-MM-dd");
    			
    			return dateButton;
    		}
    		
    	}
    
    약간의 변형으로 강력한 적응성을 보여주는 Inputter!
    아래 예제는 Level No의 Field Descriptor로 "."과 숫자만 입력받아져야 합니다. 그래서 TextInput이 새 컴포넌트를 생성하는 순간의 메서드(getNewComponent)를 override하여 Readability를 해치지 않고 간단히 그 기능을 구현했습니다.
    	...
    	new FieldDescriptor("LEVELNO",		"LEVEL",	12,	false, null,
    
    		// level no는 '.'과 숫자만 받음
    		new TextInput(){
    			public Component getNewComponent(){
    				CodeField codeField = new CodeField(10);
    			
    				codeField.setAllowAlphabet(false);
    				codeField.setAllowSpace(false);
    				codeField.setAllowingCharset(".");
    		
    				return codeField;
    			}
    		}
    	)
    	...
    
  9. 일관화된 검사방법 - Validator
  10. 입력받은 필드값을 검사(invalidate; validation)하기 위한 일관적인 interface를 제공한다. 특히, Validator를 여러개 세팅한 조합에 의한 validation은 거의 모든 validation rule을 커버할 수 있으며 InputForm에 의해 자동으로 에러 메시징이 처리된다.

    기본으로 제공되는 Validator들(com.cwpdm.util.db.validator.*)
    Validator class명 설명 default type(세팅하지 않아도 자동으로 선택되는 Type
    NotNullValid 내용이 있는지(null인지) 체크 (db의 "not null" 제약) 업슴
    NumberValid 숫자가 입력되었는지 체크 Types.INTEGER

    사용법
    Field Descriptor 의 7번째 인자로 입력한다.

    내 Validator 만들기
    AbstractValidator 의 Flow Diagram

            1. isValid(data)를 call해서 data가 맞는지 확인한다.
            2. 틀리면 throw new Exception(getErrorMessage())한다.
            3. InputForm은 (validator를 사용한 class) Exception이 나게 된다.
     

    구현예제

            public static void main(String args[]){
                    new InputForm(
                            new Table(
                                    "validator test",
                                    new FieldDescriptor[]{
                                            new FieldDescriptor("userid", "사용자 ID", 0, true, null, null, 
                                                    new Validator[]{
                                                            // 길이 체크
                                                            new AbstractValidator(){
                                                                    public boolean isValid(Object data){
                                                                            return ((String)data).length() > 10;
                                                                    }
                                                                    
                                                                    public String getErrorMessage(){
                                                                            return "10자 이하여야 합니다";
                                                                    }
                                                            },
                                                            // 빈칸 체크
                                                            new AbstractValidator(){
                                                                    public boolean isValid(Object data){
                                                                            return ((String)data).indexOf(" ") == -1;
                                                                    }
                                                                    
                                                                    public String getErrorMessage(){
                                                                            return "빈칸이 허용되지 않습니다.";
                                                                    }
                                                            }
                                                    }
                                            )
                                    }
                            )
                    ).postInputDialog(null);
            }                                       
     
  11. FieldAttribute의 활용
  12. FieldAttribute는 다음과 같은 용도로 사용됩니다.

    Example)

            public static void main(String args[]) throws Exception{
    
                    // table을 생성한다. db의 'create'문을 연상
                    Table px_part_newparts = new Table(
                            "px_part_newparts",     //  table명
                                                    //  column              title           type            iskey
                            new FieldDescriptor[]{
                                    new FieldDescriptor("NAME",             "이름",         Types.VARCHAR,  true,
                                            new FieldAttribute[]{
                                                    new FieldAttribute("default", new String('pongsor')),	//◀◀◀◀◀
                                                    new FieldAttribute("size", new Integer(10))  //◀◀◀◀◀
                                            },
                                    ),
                                    new FieldDescriptor("DATE",             "입력일",         Types.DATE,  false,
                                            new FieldAttribute[]{
                                                    new FieldAttribute("source", new String('sysdate')),	//◀◀◀◀◀
                                            }
                                    )
                            }
                    );
            }               
    
     
    Attibute Keys
    key 값 설명 사용예
    default InputForm 생성시 초기값 new FieldAttribute("default", "남자"))
    size 입력받는 컴포넌트의 최대 입력값 사이즈 new FieldAttribute("size", new Integer(10))
    hidden 숨겨진 필드로 InputForm생성시 나타나지 않음. 주로 값을 처리해서 넣을 때 사용. 세팅한 값은 디폴트로 사용됨 new FieldAttribute("hidden", "test")
    mandatory 필수 입력 필드임. 입력 컴포넌트에 붉은 세모가 생김 new FieldAttribute("mandatory", new Boolean(true))
    source SQL처리시 세팅한 String을 그대로 SQL로서 사용한다. 주로 DB server 날짜(sysdate), 시퀀스등의 표현을 바로 쓰면 유용하다.new FieldAttribute("source", "sysdate")
    dontcare 모든 처리에 대상이 되지 않는다 new FieldAttribute("dontcare", new Boolean(true))

    ※ 모든 값은 Object 형으로 입력되어야 하기 때문에 primitive type을 사용해서는 안된다. 예를 들어 수치를 입력할 때는 'new Integer(값)'으로 입력해야 한다.

  13. 상속을 통한 다양한 활용법