오류 상황 : mybatis 도입 후 테스트 프로젝트에서는 문제 없이 작동했던 부분이 원본 프로젝트 소스와 merge 하고 나니 오류 발생. 1, 2, 3, 4 쿼리를 순차적으로 타고 결과를 도출해야 하는데 1번 쿼리 실행 후 catch문 로그도 안 찍고 갑자기 finally로 빠져버림.

트러블슈팅 : ① 오류 로그라도 뭔가 확인이 가능해야 버그를 잡아내는데 아무런 로그가 안찍히니 답답했다. 그래서 할 수 없이 디버깅 모드로 실행 후 오류나기 바로 전 메소드에 toggle 걸어주고 한 step씩 넘기면서 모든 변수 값 다 확인.
② log4j 관련 변수였나? 정확한 기억은 안나지만 특정 변수에 java.lang.NoSuchMethodError: org.apache.log4j.Logger.isTraceEnabled()Z 오류 내용이 들어감
③ 지난 번 테스트 프로젝트에서 보고 해결했으나 블로그에 안 적어놔서 다시 구글링
④ log4j 1.2.12 버전 이후로 해당 메소드가 들어가 있다는 내용 확인
⑤ log4j.jar 파일 교체(기존: 1.2.8 버전)

하지만 아직도 왜 catch문을 안 탔는지 이해가 잘 안 간다. 추후 좀 더 확인해볼 필요가 있음.

Posted by 知彼知己百戰不殆
,

오류 상황 : 쿼리 실행 시 String 값 하나만 넘기면 되는데 굳이 vo를 생성해서 넘기기도 귀찮아서 String 파라미터를 넘기는데 계속 로그에 parameters : null이 찍힘

원인 : Dao쪽을 살펴보니 selectList(statement , object )가 아니라 selectList(statement)를 호출 중

해결 : selectList(statement , object)로 Dao 메소드 변경

Posted by 知彼知己百戰不殆
,

왜 프레임워크를 안 쓰는지 모르겠으나 ㅠㅠ 쌩 java로만 돌리는 환경.
대량 조회 하는 SQL을 thread까지 사용해서 동시에 돌리니 기존에 잘 돌아가던 쿼리도 executeQuery 부분에서 OutOfMemory가 발생함ㅠㅠ

생각한 해결책
1. executeQuery의 Fetch Size 조절(현재는 40,000건씩 fetch)
2. mybatis 프레임워크 도입(Spring을 이용한 mybatis 정보는 상당히 많음 ㅠㅠ)

일단 급한 불부터 꺼야 했기에 1번 해결책으로 Fetch 사이즈를 조절해보았다. 10,000건으로 줄여보았는데도 똑같이 Heap space 에러가 발생하였다. 근데 default fetch를 쓰자니 10건씩이고...10배만 늘리자 타협하여 100건씩 fetch하니 일단은 잘 돌아간다.
하지만, 지금까지 대량건 조회는 Spring에서 rowhandler를 써왔기에 java 코드 안에 SQL이 들어가 있어서 관리하기도 불편해서 이번 기회에 java mybatis without Spring 을 도입해보기로 했다.

오류 내용
- AbstractMethodError : Method oracle/jdbc/driver/OracleResultSetImpl.isClosed()Z is abstract
Exception in thread "main" java.lang.AbstractMethodError: Method oracle/jdbc/driver/OracleResultSetImpl.isClosed()Z is abstract
        at oracle.jdbc.driver.OracleResultSetImpl.isClosed(OracleResultSetImpl.java)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at org.apache.ibatis.logging.jdbc.ResultSetLogger.invoke(ResultSetLogger.java:68)
        at cohttp://m.sun.proxy.$Proxy2.isClosed(Unknown Source)
        at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValuesForSimpleResultMap(DefaultResultSetHandler.java:364)
        at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleRowValues(DefaultResultSetHandler.java:337)
        at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSet(DefaultResultSetHandler.java:310)
        at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets(DefaultResultSetHandler.java:202)
        at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:66)
        at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:80)
        at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:65)
        at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:336)
        at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:158)
        at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:110)
        at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:90)
        at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:154)
        at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:147)
        at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:142)
        at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:75)
        at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:69)
        at dao.(~~~~~). (~~~~~) ( (~~~~~) .java:9)
        at logic.Main.main(Main.java:74)

구글링 해보니 ojdbc 버전이 안 맞아서 발생하는 오류라는 것 확인. Oracle 홈페이지에 들어가 현재 업무 환경에 맞는 jdbc는 ojdbc6.jar란 것을 확인. ㅡㅡ 시스템은 개판이면서 현재 ojdbc14를 사용 중
출처: https://www.oracle.com/database/technologies/faq-jdbc.html

 

Oracle JDBC Frequently Asked Questions

For byte data, there are three Oracle SQL types: VARCHAR2, LONG and CLOB. VARCHAR2 data is of limited length, is stored directly in a column, and is transmitted to the server in inline packets. LONG data has a much larger limit (2 Gigibytes), is stored via

www.oracle.com

https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc6/11.2.0.4

메이븐 리포지토리에 들어가 ojdbc6.jar 다운로드 후 jar파일 적용하니 mybatis로 쿼리가 동작한다!
(처음부터 rowhandler를 만들면서 테스트 하기엔 무리라고 판단되어 정말 간단한 select 1 from dual 쿼리로 조회)

File list
1. xml 패키지 밑에 mybatis-config.xml (원하는 이름으로 해도 되는지는 추후 확인 필요)
2. sqlMappers 패키지 밑에 "원하는이름"-mapper.xml (SQL 작성 파일)
3. database 패키지 밑에 "원하는이름" .java (getSqlSession() 메소드 존재)
4. dao 패키지 밑에 "원하는이름" .java(mapper id로 쿼리 실행)

to-do : 추후 정보 제거한 핵심만 간추린 소스 업로드

Posted by 知彼知己百戰不殆
,

시스템 모니터링 자동화를 하며 지난 번에 구축한 SQL Server LMS 발송 DB와 내가 만든 모니터링 자동화 프로그램을 연결시켜보려고 했다. 그러나 연동 테스트 시 아래와 같은 오류가 발생하였다.

Exception in thread "main" java.lang.UnsupportedClassVersionError: com/microsoft/sqlserver/jdbc/SQLServerDataSource has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
        at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at database.SQLServerLMSDbConn.getConnection(SQLServerLMSDbConn.java:12)
        at database.DBConnectionFactory.<init>(DBConnectionFactory.java:15)
        at database.Main.main(Main.java:21)

필요사항
- 로직 소스는 최대한 건드리지 않고 내가 필요한 DB connection을 어떤 DB든지 쉽게 갖다 쓰고 싶다.

그래서 예전부터 적용해보고 싶었던 DB connection을 가져오는 factory 패턴을 적용했고, Oracle은 문제가 되질 않았다. 하지만 SQL Server는 jdbc가 달라서 MS 홈페이지에서 다운받아서 External jar로 따로 넣어주었고, MS에서 제공한 샘플 코드를 보고 했는데도 위와 같은 오류가 발생하였다.
SQL server jdbc 다운로드 url:https://learn.microsoft.com/ko-kr/sql/connect/jdbc/download-microsoft-jdbc-driver-for-sql-server?view=sql-server-ver16

 

다운로드 - JDBC Driver for SQL Server

Microsoft JDBC Driver for SQL Server를 다운로드하여 SQL Server 및 Azure SQL Database에 연결하는 Java 애플리케이션을 개발합니다.

learn.microsoft.com

원인 : 현재 java 버전엔 위 jdbc 버전이 너무 높음
https://learn.microsoft.com/ko-kr/sql/connect/jdbc/system-requirements-for-the-jdbc-driver?view=sql-server-ver16

해결 : 다운받은 zip파일 내부에 jdbc 버전이 2개가 있어서 숫자(버전)이 낮은 거로 jar 파일 교체

 

시스템 요구 사항 - JDBC Driver for SQL Server

JDBC 드라이버 시스템 요구 사항을 찾습니다. 지원되는 Java, 운영 시스템 및 데이터베이스 버전을 포함합니다.

learn.microsoft.com

 

Posted by 知彼知己百戰不殆
,

급 궁금해져서 찾아본 String 클래스의 주소값 확인 방법

System.identityHashCode("~~~~");
위 메소드를 println 해보면 String도 객체 주소값을 찍어볼 수 있다.

 

Posted by 知彼知己百戰不殆
,

Thread 기초

언어/JAVA 2023. 12. 17. 10:44

1. Thread Class와 Runnable Interface 구현 2가지 방법 존재
  - Thread Class 상속 시 다른 Class 상속 불가
  - Runnable Interface 구현 시 다른 Class 상속 가능

Thread 클래스 상속 (출처:Java 18 API docs)
Runnable Interface 구현 시(출처:Java 18 API docs)


출처 : https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/Thread.html

 

Thread (Java SE 18 & JDK 18)

All Implemented Interfaces: Runnable Direct Known Subclasses: ForkJoinWorkerThread A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently. Every thread has a

docs.oracle.com

2. start()와 run() 호출 차이
  - thread의 run()을 호출하는 것은 class에 선언된 메서드를 호출하는 것
  - start()를 호출하는 것은 새로운 thread가 작업을 실행하는데 필요한 call stack을 생성하고, run()을 호출해서 생성된 call stack에 run()이 첫 번째로 올라가게 됨

테스트용 thread 클래스
run() 메소드 호출(단순 메소드 호출) 시 call stack의 첫 메소드가 main메소드
start() 메소드 호출 시 main 메소드 thread에는 영향을 미치지 않음

3. 데몬 스레드(daemon thread)
  - 일반 스레드의 작업을 돕는 보저 역할
  - 일반 스레드가 종료되면 데몬 스레드는 강제 종료
  - "thread변수명".start(); 를 호출하기 전 "thread변수명".setDaemon(boolean 값); 을 해야 데몬 스레드로 지정

4. sleep() 사용법(?)
  - "thread변수명".sleep(~~); 으로 선언해도 되나 sleep() 메소드는 static 메소드이기 때문에 내가 지정한 스레드가 현재 실행중인 thread가 아니라면 main thread가 영향을 받음
  - Thread.sleep(); 으로 호출해야 의미 전달에 혼선이 없음

Posted by 知彼知己百戰不殆
,
실행 환경 : sql developer에서 조회 시 2분 정도 걸리는 쿼리(1달치 조회 시 약 800만건 조금 넘게 존재)
1. createstatement 후 setFetchSize 설정
2. executeQuery 실행 후 바로 다음 라인에 Fetch Size 콘솔 로그 남김. (executeQuery 바로 윗 줄에는 로그 파일에 조회 날짜 조건 출력)
3. fetch size 확인 로그는 execute 실행 후 한 참 뒤에 콘솔 로그 출력. 콘솔 로그 출력 전에는 결과 출력 file size = 0
4. fetch size 로그 출력 후 바로 file size 증가하는 것 확인.
위의 상황으로 볼 때 rs.next() 시 쿼리를 실행하는 것이 아니라 Statement.executeQuery 실행 시 쿼리 결과 값을 갖고 있는 것으로 추정.(약 800만건의 데이터를 텍스트 파일로 저장 시 약 2.06gb)
그리고 파일 쓰기가 계속 진행되고 있는 동안 로그 파일에 조회 날짜 출력을 안 함
여기서 궁금한 점은 2기가나 되는 데이터를 어디에 들고 있다가 파일로 쓰기 시작하는 것인지 궁금.
 
참고 URL

2. https://www.ibm.com/docs/en/db2-for-zos/11?topic=applications-retrieving-data-from-tables-using-statementexecutequery-method

 

DB2 11 - Java - Retrieving data from tables using the Statement.executeQuery method

To retrieve data from a table using a SELECT statement with no parameter markers, you can use the Statement.executeQuery method. About this taskThis method returns a result table in a ResultSet object. After you obtain the result table, you need to use Res

www.ibm.com

3. https://docs.oracle.com/cd/E11882_01/java.112/e16548/resltset.htm#JJDBC28615

 

Result Set

28/54 17 Result Set Standard Java Database Connectivity (JDBC) features in Java Development Kit (JDK) include enhancements to result set functionality, such as processing forward or backward, positioning relatively or absolutely, seeing changes to the data

docs.oracle.com

 

'언어 > JAVA' 카테고리의 다른 글

String의 JVM에 들어가있는 주소값 출력  (1) 2023.12.17
Thread 기초  (0) 2023.12.17
class 초기화 블럭(initialization block)  (0) 2023.09.10
인스턴스 변수와 this  (0) 2023.09.10
클래스 상속과 메소드 오버라이딩  (0) 2023.05.06
Posted by 知彼知己百戰不殆
,

Test 클래스의 초기화 블럭과 기본 생성자를 만들어 줌

초기화 블럭도 만들어줬으나 생성자의 초기화로 값이 덮어짐

기본 생성자 코드 제거 후

코드를 실행하니 초기화 블럭의 초기값 세팅 정상 작동

그럼 만약 생성자에서 초기화 변수를 하나 제거한다면???

예상대로 생성자 초기화에서 없는 부분이 초기화블럭에 있다면 초기화 블럭의 초기값이 그대로 들어감

'언어 > JAVA' 카테고리의 다른 글

Thread 기초  (0) 2023.12.17
executeQuery와 Fetch size  (0) 2023.12.13
인스턴스 변수와 this  (0) 2023.09.10
클래스 상속과 메소드 오버라이딩  (0) 2023.05.06
Math.random과 Random 클래스 차이  (0) 2022.11.18
Posted by 知彼知己百戰不殆
,

• Test 클래스의 붉은색 네모 부분이 인스턴스변수
인스턴스변수에 접근할 땐 this 키워드로 접근

this 키워드는 참조변수로 인스턴스 자신을 가리킴.
❌ static 메서드에서는 인스턴스멤버들 사용 불가. static 메서드가 호출된 시점에 인스턴스 멤버가 메모리에 존재하지 않을 수 있음

Posted by 知彼知己百戰不殆
,
public class Main {
	public static void main(String[] args) {
		ParentsClass pc = new ChildClass();
		pc.a();
	}
}

class ParentsClass {
	public void a() {
		System.out.println("Parensts a() call");
		b();
		System.out.println("Parents a()");
	}
	public void b() {
		System.out.println("Parensts b() call");
		a();
		System.out.println("Parents b()");
	}
}

class ChildClass extends ParentsClass{
	public void a() {
		System.out.println("Child a() call");
		super.a();
		System.out.println("Child a()");
	}
	public void b() {
		System.out.println("Child b() call");
		System.out.println("Child b()");
	}
}

main함수에서 Parents 타입의 변수에 Child 클래스(Parents 상속)의 메소드 호출 시 호출 결과

Child클래스에서 super로 Parents 클래스의 a 메소드를 호출 했을 때 Parents 클래스의 a 메소드에서 b 메소드 호출.
머릿속으로는 Parents 클래스의 b 메소드가 호출될 줄 알았지만 Child 클래스의 b메소드가 호출되었다.

메소드 오버라이딩 하면 부모 클래스에서 호출하는 메소드가 자식 클래스에도 있으면 자식 클래스의 메소드가 우선 호출되는 것으로 보임.

 ❗️Child 클래스에서 b메소드를 주석처리 후 Parents 클래스의 b 메소드 안에 a()만 주석 처리 후 실행했더니

위와 같은 결과가 도출되었다.
메소드 오버라이딩이 없으면 부모 클래스에서 자식 클래스에 없는 메소드를 호출하면 부모 클래스의 메소드가 실행되었음

1년 동안 Java를 안 건드리다가 오랜만에 Java를 다시 보니 기억이 가물가물 ㅠㅠ

생성자 관련해서 예전에 썼던 글도 참고하면 좋을 듯

https://blair6383.tistory.com/249

 

클래스 상속과 클래스 타입 참조변수 확인

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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 public class GrandParents { String pStr = "grand parents"; GrandParent

blair6383.tistory.com

 

'언어 > JAVA' 카테고리의 다른 글

class 초기화 블럭(initialization block)  (0) 2023.09.10
인스턴스 변수와 this  (0) 2023.09.10
Math.random과 Random 클래스 차이  (0) 2022.11.18
Scanner객체 NoSuchElementException 오류  (0) 2021.06.20
Java 버전별 달라진 점.  (0) 2020.07.22
Posted by 知彼知己百戰不殆
,