You are currently browsing the category archive for the 'Devlog' category.
DBMS LOG용 디렉토리 생성
CREATE OR REPLACE DIRECTORY DIR_BDUMP
AS
'/oracle/admin/SID/bdump/';
DBMS LOG 외부 테이블 생성
CREATE TABLE DBMSLOG (
LOG_TEXT VARCHAR2(4000)
)
ORGANIZATION EXTERNAL (
TYPE ORACLE_LOADER
DEFAULT DIRECTORY DIR_BDUMP
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
FIELDS TERMINATED BY '~'
MISSING FIELD VALUES ARE NULL (
LOG_TEXT CHAR(4000)
)
)
LOCATION ('alert_SID.log')
)
REJECT LIMIT UNLIMITED;
DBMS LOG 뷰 생성
CREATE OR REPLACE VIEW DBMSLOG_VI
AS
SELECT LOG_RNUM
, LAST_VALUE(LOG_LNUM IGNORE NULLS)
OVER(ORDER BY LOG_RNUM ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW) LOG_SNUM
, LAST_VALUE(LOG_DATE IGNORE NULLS)
OVER(ORDER BY LOG_RNUM ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW) LOG_DATE
, TRC_FILE
, LOG_TEXT
FROM (SELECT ROWNUM LOG_RNUM
, NVL2(LOG_DATE, ROWNUM, NULL) LOG_LNUM
, LOG_DATE
, TRC_FILE
, LOG_TEXT
FROM (SELECT CASE REGEXP_INSTR(LOG_TEXT,
'[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}'
|| ' [[:digit:]]{4}')
WHEN 0 THEN NULL
ELSE TO_DATE(LOG_TEXT
, 'Dy Mon DD HH24:MI:SS YYYY'
, 'NLS_DATE_LANGUAGE=AMERICAN')
END LOG_DATE
, CASE REGEXP_INSTR(LOG_TEXT, '^Errors in file')
WHEN 1 THEN REGEXP_REPLACE(LOG_TEXT
, '^Errors in file (.*):$'
, '\1')
ELSE NULL
END TRC_FILE
, LOG_TEXT
FROM DBMSLOG));
COMMENT ON TABLE DBMSLOG_VI IS 'DBMS Alert 로그 뷰';
COMMENT ON COLUMN DBMSLOG_VI.LOG_RNUM IS 'DBMS Alert 로그 순번';
COMMENT ON COLUMN DBMSLOG_VI.LOG_SNUM IS 'DBMS Alert 로그 시작 순번 그룹';
COMMENT ON COLUMN DBMSLOG_VI.LOG_DATE IS 'DBMS Alert 로그 일시';
COMMENT ON COLUMN DBMSLOG_VI.TRC_FILE IS 'DBMS Alert Trace 파일';
COMMENT ON COLUMN DBMSLOG_VI.LOG_TEXT IS 'DBMS Alert 로그 내용';
DBMS LOG 파일 조회
SELECT A.LOG_DATE
, A.TRC_FILE
, A.LOG_TEXT
FROM (SELECT TO_CHAR(MIN(LOG_DATE), 'YYYY-MM-DD HH24:MI:SS') LOG_DATE
, SUBSTR(XMLAGG(XMLELEMENT(TEMP, CHR(10) || TRC_FILE)
ORDER BY LOG_RNUM)
.EXTRACT('//text()').GETSTRINGVAL(), 2) TRC_FILE
, SUBSTR(XMLAGG(XMLELEMENT(TEMP, CHR(10) || LOG_TEXT)
ORDER BY LOG_RNUM)
.EXTRACT('//text()').GETSTRINGVAL(), 2) LOG_TEXT
FROM DBMSLOG_VI
WHERE LOG_DATE BETWEEN TRUNC(SYSDATE -
AY)
AND SYSDATE -
AY
GROUP BY LOG_SNUM
ORDER BY LOG_SNUM DESC) A
WHERE LOG_TEXT LIKE '%' || :TEXT || '%';
참고
Query the Oracle Alert Log using SQL commands
Oracle Regular Expression(정규표현식)
iBATIS에서 질의를 만들다 보면 다음과 같은 오류가 난다.
ORA-00911: 문자가 부적합합니다
ORA-00911: invalid Character
실제 내용이된 SQL이 잘못된 것이 아니라면 제일 마지막에 세미콜론(“;”)을 붙이지 않았나 의심해 보자.
CLOB를 선택해야 하는 문제로 테스트 중에 SELECT 문에서 나는 위와 같은 오류를 TypeHandler를 잘못 등록했거나 JDBC 드라이버, NativeJdbcExtractor, LobHander 문제로 착각하고 몇 시간을 헤렸다. -_-;;
이름 변경 문법
alter table info rename constraints pk_info to pk_info_old;
alter index pk_info rename to pk_info_old;
alter table info rename to info_old;
LOB 생성 문법
create table info
(
id varchar2(40) not null,
pid varchar2(40) not null,
type varchar2(2) not null,
desc varchar2(200),
value clob,
creator varchar2(20) not null,
created date default sysdate not null,
updater varchar2(20) not null,
updated date default sysdate not null
)
tablespace tblspace
pctused 40
pctfree 10
lob (domain_prop_value) store as (
index lx_info_01 (tablespace idxspace)
tablespace tblspace
);
온라인 재정의를 통한 LONG -> LOB 변환을 하던 중 Primary Key가 없을 경우 DBMS_REDEFINITION.START_REDEF_TABLE 호출시 다음과 같은 오류가 발생하였다.
ORA-12089: cannot online redefine table “KCUBE”.”TBR_RFM_QXDMMSG” with no primary key
PK가 없는 테이블에 대해서는 안되는 모양이다.. -_-;;
추가
PK가 반드시 존재해야 하는 이유는 Table의 재정의 작업이 MV를 통해 이루어지기 때문이었다. MV를 이용하는 시점은 SYNC_INTERIM_TABLE 호출시로 온라인 작업중 변경된 사항에 대해 SYNC를 맞추는 시점이다. 재정의시 PK를 생성하고 정의가 끝나면 DROP 하는 방식으로 작업을 하면 되겠다.
참고로, PK가 아닌 ROWID 방식으로도 가능하다. DBMS_REDEFINITION.CONS_USE_PK 대신에
DBMS_REDEFINITION.CONS_USE_ROWID를 사용하면 된다.
업무 중 테이블에 대한 컬럼 드랍 요청이 있어서 작업을 하려고 하니 다음과 같은 오류가 발생하였다.
ORA-39726: unsupported add/drop column operation on compressed tables
Compress된 테이블은 컬럼 추가/삭제가 지원되지 않는다면 말이다. ALTER로 테이블을 NOCOMPRESS로 변경하여도 신규로 들어가는 데이터에 대해서만 적용되지 기존의 압축된 데이터는 그대로이기 때문에 해결책이 되지 않는다.
이럴 경우 해당 테이블의 테이블 스페이스를 다른 곳으로 이동하면서 NOCOMPRESS 옵션을 사용하면 COMPRESS 설정이 풀리게 된다. 만약 파티션의 경우도 마찬가지로 파티션이 위치하는 테이블 스페이스를 다른 곳으로 이동하면 된다. 이후 DDL 작업이 완료된 후 다시 원래의 테이블 스페이스로 COMPRESS 옵션을 주어 원복하면 작업이 완료된다.
파티션된 테이블의 경우는 다음과 같은 방식으로 스크립트 생성이 가능하다.
select 'ALTER TABLE '
|| object_name
|| ' MOVE PARTITION '
|| subobject_name
|| ' TABLESPACE TS_NEW NOCOMPRESS PARALLEL 8 NOLOGGING;'
from user_objects
where object_type = 'TABLE PARTITION'
and object_name in(select tname
from tab)
문제는 다음과 같았다.
사용자가 로그인 화면에서 로그인을 요청하게 되면 사용자 인증을 한 후 사용자 정보를 Request 객체의 Session 영역에 넣어 놓고, 이후 다시 초기 페이지 요청시 사용자 정보가 있으면 바로 본 화면으로 넘어가는 방식으로 코드를 구현하였다.
하지만 로그인 이후 자동으로 본 화면 이동시 사용자 정보를 읽지 못하는 현상을 보였다.
문제는 iBATIS에서 얻어진 결과 객체를 바로 Session에 집어 넣는 실수로 인한 것으로 보여졌다. 아마도 iBATIS의 결과 객체의 레퍼런스를 잡고 있다가 일정 시간 이후에 메모리에서 지우는 이유 때문일 것이다.
그리하여 다음과 같이 기존 코드를 변경하였다.
기존
request.getSession().setAttribute("user", user);
변경
request.getSession().setAttribute("user", new HashMap(user));
iBATIS 결과 객체의 Life Cycle이 어떤 식으로 동작하는지 찾아봐야 할 것 같다.
Spring Framework을 사용하면서, IoC 기반의 코딩을 하기 위해 Interface를 앞단에 두고 이를 구현하는 방식으로 구현한 소스를 좀 뒤집었다.
추가적인 이종의 구현의 필요가 없어 개발의 편의성을 위해 앞단의 Interface를 모두 제거하는 식으로 변경을 하였다. Interface를 둘 경우 Eclipse 소스에서 Ctrl 키를 눌러 참조 구현부로 이동하는 기능이 Interface로 연결되면서 개발상 불편한 것도 변경의 이유가 되었다.
문제는 앞단의 Interface를 지우고 직접 Class를 올리게 되니 Spring에서 Bean을 생성하고 Proxy AOP를 거는 중 다음과 같은 오류가 발생하였다.
Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces.
인터넷상에 나와있는 “JDK Dynamic Proxy와 CGLIB Proxy에 대한 이해“를 보면 2가지 종류의 Proxy가 이용되는 것으로 확인된다. 아마도 이전에 Interface 기반에서 JDK Dynamic Proxy가 사용된 것으로 보인다.
직접 Class에 대한 Proxy를 이용하기 위해서 Spring Framework의 “lib/cglib” 위치에 있는 “cglib-nodep-2.1_3.jar” 라이브러리를 Class Path에 추가해 주면 된다.
Log4J의 설정에서 로그 파일의 경로를 지정하는 부분이 있다. 대부분의 예제에서는 절대 경로로 설정이 되어 있어서 개발시와 서버 운영시에 log4j.xml 파일을 별도로 관리하여야 하는 어려움이 있었다.
때문에 여러가지 방법을 테스트 하던 중 다음과 같은 방식으로 Context Root를 기준으로 하는 상대 경로 설정법이 동작하는 것을 확인하였다.
해당 Appender의 설정 부분은 다음과 같다.
<appender name="spring" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="${webapp.root}/WEB-INF/logs/spring.log"/>
<param name="Append" value="false"/>
<param name="DatePattern" value="'.'E"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{MM-dd HH:mm:ss} %-5p %c{2} - %m%n"/>
</layout>
</appender>
추가
위에서 webapp.root는 web.xml에서 정해주는 webAppRootKey와 관련이 있다. 기본값이 webapp.root일 뿐이다. 다음과 같은 코드로 정의가 가능하다.
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>my.root</param-value>
</context-param>
Flex에서 기존 웹 페이지를 팝업으로 띄우기 위해서는 보통 다음과 같은 방식을 사용한다.
navigateToURL(new URLRequest('http://wordpress.com'), '_blank');
하지만 위 방법으로는 Java Script에서 가능한 window.open('..', '..', '..') 기능에서와 같이 툴바와 크기, 리사이즈 등의 옵션을 주기가 어렵게 된다.
그래서 다음과 같이 원하는 옵션을 주어 팝업창을 띄우는 함수를 하나 만들어 사용하고 있다.
public function openWeb(url:String, name:String, width:Number, height:Number):void {
navigateToURL(new URLRequest('javascript:(function(){window.open("' + url
+ '", "' + name + '", "toolbar=no,location=no,status=no,'
+ 'menubar=no,scrollbars=no,resizable=no,width=' + width
+ ',height=' + height + '");})()'), '_self');
}
Tomcat 환경에서 Spring Framework으로 설정을 끝마치고 즐거운 마음으로 실행을 시켜 보았는데, POJO 클래스의 생성자가 두 번씩 호출되는 현상이 발생하였다. <bean/> 선언에 scope="singleton"을 넣어 보아도 마찬가지 현상이고, Spring Framework의 debug 로그를 보아도 두 번씩 객체를 생성하는 기이한 구조를 보이고 있었다.
이것 저것 설정을 건드리다가 범인을 발견!! 범인은 다름 아닌 <import/>로 분산한 IoC 설정 파일이었다.
<import resource="sub-spring.xml"/>
위와 같이 분리하였을 경우 sub-spring.xml에 선언된 Bean은 생성자가 두 번씩 호출된다. init-method로 설정한 것도 두 번씩 호출된다.
이것을 피하기 위해 <import/>로 분리한 것을 web.xml의 contextConfigLocation에 몰아서 선언하는 것으로 문제를 해결하였다.
