환경 : Springframework 3.1.1 Release, Oracle 11g Database, JDK 1.8, mybatis

오류 발생 상황:
대량 text 파일 데이터 (약 118만 row)를 read 후 database insert 테스트 시 종종 일정 개수만 insert 후 더 이상 진행 불가 현상 발생. lock 확인 해 보니 v$session 테이블의 WAIT_CLASS 컬럼에 Concurrency가 찍혀있었고, v$session_blockers테이블의 WAIT_EVENT_TEXT 컬럼에 library cache: mutex X 가 찍힘.

원인 : 다양한 원인이 있으나 Bulk insert로 진행하더라도 (3000건씩 insert 하도록 했었음) 394번이 넘는 insert를 해야해서 Hard Pasing쪽이 큰 원인으로 판단

해결 : 일반 sqlSessionTemplate에서 진행하지 않고, insertBatch를 활용하여 insert.
          applicationContext.xml에 Batch로 진행할 sqlSessionTemplate bean을 하나 더 생성해준 후 Executortype을 BATCH로 설정. service 단에서 batch템플릿을 injection 후 
         sqlSessionTemplateBatch(내가 설정한 변수명).insert("mapper의 namespace.메소드명", insert파라미터); 
        그리고 파일 1개당 batch flush를 해주기 위해 파일 하나가 끝날 때마다 sqlSessionTemplateBatch.flushStatements();호출
 batch insert 실행 시 10만건에 3~4초 시간 소요.

Posted by 知彼知己百戰不殆
,

개발환경 :
  - SpringFramework 3.1.1. Release
  - JDK1.8

순수 java code에서 사용할 땐 ThreadPoolExecutor을 사용했으나, 현재 프로젝트는 Spring을 사용하고 있기에 Spring에서 관리가 가능한 ThreadPoolTaskExecutor을 적용하며 OOM이 나지 않기 위해 각 스레드에서 mybatis의 resultHandler를 사용하기로 함

해당 기능 적용 목적 : 문자 발송이 승인된 건은 문자 발송할 양에 따라 쓰레드 개수를 나눠서 분할 처리
(단일 스레드 처리 시 약 13만건 발송 시 22시간 소요) => 분할처리 적용 후 약 13만 건 발송 시 4시간 30분 소요 대략 80%정도의 시간 감소 효과 발생

1. applicationContext.xml 설정
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
  <property name="corePoolSize" value="지정 스레드수"/>
  <property name="queueCapacity" value="태스크 대기 수"/>
  (이외에도 maxPoolSize 등 다른 설정할 것이 있으니 따로 확인)
</bean>

2. java Service.java 
@Autowired
private ThreadPoolTaskExecutor taskExecutor; 

(개인 프로젝트가 아니라 필요한 부분만 발췌하여 간략히 작성)
public void sendSMS() {
~~~ 처리할 양에 따른 적용할 스레드 수 계산로직 ~~~
taskExecutor.setCorePoolSize(스레드 수);
for(~~~~) {
  taskExecutor.submit(new SMSThread(필요한 파라미터 변수));
}
}

3. SMSThread.java
Runnable 인터페이스 구현
public void run() {
  mapper.selectSMSHandler(~~, new ResultHandler() {
   @Override
   public void handleResult(ResultContext context) {
     try {
        SMSdto rowData = (SMSdto) context.getResultObject();
        ~~~ row별로 읽은 데이터 처리 로직 ~~~
     } catch () {}
   }
});

 

[발생했던 문제]

1. ResultHandler를 사용했으나 오류 로그도 안 남고(? 정확하게 기억이 안남 ㅠㅠ) 읽은 row에 대해 처리 로직 수행이 안되는 경우
  => ResultHandler 안에서 try catch 구문을 사용하여 오류 확인. 기존에 테스트를 하며 현재 사용 안하는 변수가 있었는데 해당 변수에서 값을 참조하여 log찍는 곳에서 오류 발생하여 해당 변수 삭제.

2. 1건에 대한 처리는 완벽하게 수행되나 2건 째부터는 TaskRejectedExeption 발생
  => 태스크 대기 큐 설정을 안해서 그런 줄 알고 remainingCapacity()를 찍어보았으나 해당 없음.(0은 대기 큐 미사용이었으나 나의 경우 2로 출력)
       shutdown 메소드를 사용한 경우 추가적인 태스크를 받을 수 없다는 걸 확인. isShutDown()메소드로 확인 해봤더니 true로 출력....? 코드를 봤더니 기존 java 프로젝에서 테스트 하던 코드를 그대로 들고왔던 곳에서 shutdown 코드가 남아있는 것을 발견. 해당 코드 삭제 후 대기 큐에 정상적으로 들어와서 처리 완료된 것 확인

Posted by 知彼知己百戰不殆
,

개발 환경 : 
  -SpringFramework 3.1.1. Release

1. applicationContext.xml 파일 설정(굵은/붉은 글씨들은 각자의 상황에 맞게 변경 가능한 변수명 개념)

<bean id ="dataSource1" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
  <property name="driverClass" vlaue="oracle.jdbc.OracleDriver"/>
  < property name="url" vlaue="jdbc:oracle:thin:@IP:PORT:SID"/>
  <property name="username" vlaue="아이디"/>
  <property name="password" vlaue="비밀번호"/>
</bean>

<bean id="dataSource2" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
  <property name="driverClass" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
  <property name="url" value="jdbc:sqlserver://IP:PORT;databaseName=데이터베이스명;trustServerCertificate=true"/>
  <property name="username" value="아이디"/>
  <property name="password" value="비밀번호"/>  
</bean>
 

<bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource1"/>
  <property name="mapperLocations" value="classpath*:매퍼가 들어가 있는 패키지명/*.xml"/>
</bean>
<bean id="sqlSessionFactory2" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource2"/>
  <property name="mapperLocations" value="classpath*:2번째DB를 연결할 매퍼가 들어가 있는 패키지명/*.xml"/>
</bean>

<bean id="sqlSessionTemplate1" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg ref="sqlSessionFactory1"></constructor-arg>
</bean>
<bean id="sqlSessionTemplate2" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg ref="sqlSessionFactory2"></constructor-arg>
</bean>

<bean id="1MapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="1번 database연결할 패키지위치"/>
  <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate1"/>
</bean>
<bean id="2MapperScannerConfigurer" class="~~~>
  ~~~~
</bean>

<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource1"/>
</bean>
<bean id="transactionManager2" class="~~나머지는 동일>
</bean>

<tx:annotation-driven transaction-manager="transactionManager1"/>
<tx:annotation-driven ~~동일/>

발생했던 오류 상황

1. 2번째 Database를 사용하는 mapper bean injection 시 2개의 sessionFactory가 발견되어 매칭 불가 상황

  => mapper에 2번째 DB sessionFactory를 강제 주입(Qualifier 어노테이션 이용)
<bean id="Mapper bean명" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="dataSource2를 사용하는 Mapper 인터페이스 위치"/>
  <property name="sqlSessionFactory" ref=sqlSessionFactory2"/>
</bean>
 그리고 mapper interface위에 Qualifier("Mapper bean명") 설정하여 해결 완료

Posted by 知彼知己百戰不殆
,

대고객 문자 발송 시스템 개발 중 트랜잭션 설계를 하며 트랜잭션 전파 속성, DB pessimistic lock 을 테스트 중이다.

삽질 내용 : 로직(Required) 윗단에 for update wait로 lock을 잡아놓고 밑에 DB update 로직을 REQUIRES_NEW로 처리하면 당연히 될거라 생각. 그런데 계속 멈춤 현상이 발생. 왜 그럴까 생각해보니 이미 한 트랜잭션에서 select lock을 잡아놓고 새로운 트랜잭션에서 Update를 하려고 하니 당연히 lock 걸려 있으니 안되는게 정상...

DB pessimistic lock 기능 잘 작동되네...ㅋ

Posted by 知彼知己百戰不殆
,

문제 : 스프링프레임워크를 사용 중인 프로젝트에서 오류 발생 시 rollback 처리가 되지 않음

원인 분석:
1. 메소드에 transactional 어노테이션이 추가되어 있으므로 당연히 오류 발생 시 rollback 될거라 생각했으나, 일단 기본 어노테이션 속성은 예기치 못한 오류에 대해서 rollback하므로 어노테이션에 rollback속성 지정
2. rollback 속성을 지정했음에도 rollback이 실행되지 않았고, 그대로 DB update로 이어짐
3. transaction이 생성되지 않고 그냥 처리된단 생각이 들어 application-context.xml 확인

해결 방안 : <tx:annotation-driven transaction-manager="transactionManager"/> 설정이 없어 추가

참고: annotation-driven: 등록된 bean 중 transactional 어노테이션이 붙은 것들에 transaction 적용

Posted by 知彼知己百戰不殆
,

***************************
APPLICATION FAILED TO START
***************************
Description: Failed to bind properties under 'spring.datasource.hikari' to com.zaxxer.hikari.HikariConfig: Property: spring.datasource.hikari.driver-class-name Value: net.sf.log4jdbc.sql.jdbcapi.DriverSpy Origin: class path resource [application.properties]:4:44 Reason: Failed to load driver class net.sf.log4jdbc.sql.jdbcapi.DriverSpy in either of HikariConfig class loader or Thread context classloader

구글링을 해도 gradle을 새로고침 하라는 얘기밖에 없었다. 하지만 난 이미 jar파일을 가지고 있었고, devtool로 스프링부트를 구동중이었으므로 변경되면 자동으로 스프링부트가 업데이트 되므로 당연히 적용이 돼있을 줄 알았다. 하지만 계속 에러가 나길래 서버를 완전히 끄고 재기동 했더니 잘 올라간다...

devtool로 실행해도 gradle로 새로 가져온 jar파일은 바로 적용이 안되는 것 같다.

Posted by 知彼知己百戰不殆
,

오류 1. 한글 깨짐 현상
제목, 내용에 한글을 입력하고 index페이지를 호출해봤으나 한글이 <?>이런식으로 모두 깨져서 알아볼 수 없게 출력.
구글링 했더니 모두들 charset을 UTF-8로 지정하면 된다는 말만 가득. 그러다가 어느 블로그를 봤더니 전체 파일 encoding을 UTF-8로 지정하라는 글을 봄. 

파일의 encoding을 UTF-8로 지정하니 잘 나왔다.

오류 2. 언젠간....

Posted by 知彼知己百戰不殆
,

1. Getter, Setter 어노테이션 사용을 했으나, 소스에서 getter오류가 날 때

롬복을 library에 넣어서만 되는게 아니라 설치를 해야 오류가 나지 않는다.
cmd창을 이용해 lombok jar파일이 있는 위치로 이동한다.

java -jar 파일명을 이용하여 jar파일 실행 후 IDE 재기동.

IDE 셋팅

STS에서 help → Install new software → https://projectlombok.org/p2 입력 → Lombok 설치파일 설치

Posted by 知彼知己百戰不殆
,

RestController 클래스를 만들고 어노테이션을 @RestController로 입력. 그러나 빨간줄로 restcontroller is not an annotation type 오류가 나며 해당 오류 내용이 나오며 import로는 불가능하고 @org.springFramework.~~ 형태로 어노테이션을 작성해야 했다.

뭔가 이상해서 구글링을 해보니 어노테이션 안의 RestController 클래스와 충돌이 발생해서 그렇다는 내용 확인.

그래서 Class이름을 RestApiController로 변경 후 에러 잡음.

 

Posted by 知彼知己百戰不殆
,

cmd창에서 mysql -u root -p 입력 시 'mysql'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는
배치 파일이 아닙니다. 오류발생.

구글링을 통해 검색해본 결과 시스템 환경변수 path에 경로가 없어서 그렇다는 내용 확인.

path에 C:\Program Files\MySQL\MySQL Server 8.0\bin 추가

cmd접속 시도 결과 : 성공

Posted by 知彼知己百戰不殆
,