728x90
300x250

[GNU(리눅스[Linux]) - Oracle VirtualBox(GNU/GPL v2) 리눅스 설치 그리고 ova 백업과 복원


이번에 소개할 내용은 리눅스에서 Oracle VirtualBox(GNU/GPL v2)을 사용하는 방법에 대해 소개하겠다.


This time, I will introduce how to use Oracle VirtualBox (GNU/GPL v2) in Linux.



1. 소개(Introduce)


Oracle VirtualBox(GNU/GPL v2)를 리눅스에서 설치하는 방법에 관한 것이다.


How to install Oracle VirtualBox (GNU/GPL v2) on Linux.


그림 1. Oracle VirtualBox 리눅스에서 설치하기


아래의 그림은 Oracle VirtualBox를 통해서 OVA 파일(백업, 복원)에 관한 방법을 소개하고 있다.


The figure below introduces the method of OVA file (backup, restore) through Oracle VirtualBox.




그림 2. Oracle VirtualBox - 백업



그림 3. Oracle VirtualBox - 복원



2. 첨부(Attachment)


201225_Oracle_VirtualBox_Linux_installation_guide.zip

201225_Oracle_VirtualBox_ova_backup_and_restore_guide.zip


[GNU/GPL v3 License를 적용 받는다.]



* 맺음글(Conclusion)


Oracle VirtualBox를 리눅스에서 사용하는 방법과 OVA파일 형태로 백업과 복원을 하는 방법에 대해서 소개하였다.


It introduces the method of how to use Oracle VirtualBox on Linux and OVA backup and restore files.



* 참고 자료(Reference)


1. OracleVM VirtualBox, https://www.virtualbox.org, Accessed by 2021-01-01, Last Modified 2021-01-01.

반응형
728x90
300x250
[PC활용] Oracle(오라클) - Cmd 명령어로 sqlplus가 동작하지 않을 때 해결 방법


Oracle을 PC에서 설치하여 sqlplus로 작업을 하고자 했을 때 초기 순정 설치했을 때는 sqlplus 명령어가 잘 되지만, 
살다보면 셋팅값을 변경해서 안 켜지는 경우가 있을 수도 있다.


이럴 때 해결하는 방법에 대해서 소개하려고 한다.


[작성환경]

- MS 윈도우 10

- Oracle Databases 설치된 상태


[적용대상]

- Oracle Databases



1. 제어판 - 시스템 속성


몇 가지 간단한 설정으로 다시 사용할 수 있다.


그림 1. 시스템 속성


컴퓨터 -> 속성을 클릭한다. (마우스 오른쪽 버튼)

고급 시스템 설정을 클릭한다.

고급 탭을 클릭한다.

환경 변수(N)을 클릭한다.





그림 2. 시스템 속성


시스템 변수의 "PATH"를 편집한다.

예) C:\oraclexe\app\oracle\product\11.2.0\server\bin

오라클 설치한 경로를 찾아가보면 아래의 그림처럼 sqlplus의 위치를 찾을 수 있다.



[추가 방법]

-> 1. (컴퓨터 -> 속성 -> 환경변수 -> 시스템 변수 -> 새로만들기 

      "ORACLE_HOME" -> C:\oraclexe\app\oracle\product\11.2.0\server

   2. 시스템 변수 (Path 편집) -> %ORACLE_HOME%\bin





그림 3. 오라클 서버 프로그램 폴더

해당 경로에 있는 sqlplus의 모습이다.

반응형
728x90
300x250
[Oracle Databases] 번외글 - 게시판 페이징 관련 로직 쿼리


게시글 중 페이징네이션과 관련한 글을 실습하다보면, 쿼리 문제에 당면하는 일들이 벌어질 수 있다.

그래서 따로 몇 가지 중요 포인트를 정리해보았다.


1. [JSP] 17. JSP/Servlet MVC2 - 페이징네이션과 검색 그리고 오라클 프로젝트 (1), 2020-09-30
- https://yyman.tistory.com/1428


2. [JSP] 18. JSP/Servlet MVC2 - 페이징네이션과 검색 그리고 오라클 프로젝트 (2), 2020-09-30
https://yyman.tistory.com/1429


- 사용 프로그램: SQL Developer (Oracle)

- DB: Oracle Databases [11, 19g]


이 글이 어렵게 이해되는 경우를 위해서 아주 친절하게 SQL Developer에서 어떤 작업을 했는지 소개하도록 하겠다.



그림 1. Oracle SQL Developer에서의 작업 모습의 예

그림 1은 쿼리 넣고 태스트를 하는 모습이다. 예를 들면, Java, C#, C++, PHP, .NET 등의 코드를 삽입하기 전에 미리 확인을 해볼 수 있다.

이런 작업을 수 차례 반복해서 해보고 프로그래밍 코드에 적용하면 된다.


더 좋은 전문적인 프로그램이 있으면, 또는 편리한 프로그램이 있으면 다른 프로그램을 사용해도 무방하다.



1. 게시판 페이징 로직


게시글 "[JSP] 18. JSP/Servlet MVC2 - 페이징네이션과 검색 그리고 오라클 프로젝트 (2), 2020-09-30"에 적혀져 있는 SQL 쿼리문이다.

물론 이 쿼리는 오라클 형태로 작성된 쿼리이다.


-- Oracle 11 - 자동번호 생성 테이블 정의
-- Table 생성 (BOARD)
-- NEW.ID (Table의 id를 가리킴)
CREATE TABLE board
(
    id NUMBER PRIMARY KEY,
    name VARCHAR2(30),
    subject VARCHAR2(30),
    memo NCLOB,
    count NUMBER,
    regidate DATE
);


-- Sequence 정의
CREATE SEQUENCE board_sequence
START WITH 1
INCREMENT BY 1;


-- Trigger 생성
-- BEFORE INSERT on '테이블명'
CREATE OR REPLACE TRIGGER board_trigger
BEFORE INSERT
    ON board
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT board_sequence.nextval INTO :NEW.ID FROM dual;
END;


/* 데이터 추가 */
INSERT INTO board (name, subject, memo, count, regidate) VALUES ('홍길동', '안녕하세요.', '메모메모', '0', '2020-09-29 11:11:00');

/* 데이터 등록 후 커밋할 것(대량 정보 처리 후) */
COMMIT;

-- 싱글 쿼리 (페이징)
SELECT * FROM (
SELECT /*+ INDEX_DESC(Z OP_SAMPLE_PK) */ ROWNUM AS RNUM, Z.* FROM (
SELECT * from board order by id desc
) Z WHERE ROWNUM <= 10
) WHERE RNUM >= 1

-- 특정 싱글 쿼리 SQL
SELECT * FROM (
SELECT /*+ INDEX_DESC(Z OP_SAMPLE_PK) */ ROWNUM AS RNUM, Z.* FROM (
SELECT * from board where subject like '%야해해%' order by id desc
) Z WHERE ROWNUM <= 10
) WHERE RNUM >= 1


[파일명: board-tbl-oracle_개선후.sql]


[첨부(Attachments)]

board-tbl-oracle_개선후.zip


이 쿼리처럼 세상에 있는 문제가 단일로 처리되면 간단하게 구성해도 된다.

하지만, 그렇지 않다는 점이다.




2. 다중 테이블 문제


두 개의 테이블을 두었다.


하나는 메뉴와 가격에 대한 테이블이다.

하나는 가게 정보에 대한 테이블이다.


CREATE TABLE foodmenu_tbl
(
    id NUMBER PRIMARY KEY,
    name VARCHAR2(30),
    price NUMBER,
    store_id NUMBER,
    cnt NUMBER,
    regidate DATE
);

-- Table 생성 (FOODSTORE_TBL)
CREATE TABLE foodstore_tbl
(
    id NUMBER PRIMARY KEY,
    name VARCHAR2(30),
    address VARCHAR2(30),
    regidate DATE
);


두 개 테이블을 하나로 합치면, foomenu_tbl의 stord_id와 foodstore_tbl의 id가 참조 관계를 형성하는 것을 알 수 있다.

모델링 프로그램으로 살펴볼 때는 외래키 지정해도 무방하다.


아무튼, 두 가지 이상 테이블을 조인하여 사용했을 때 쿼리에 대한 것이다.

100만 건 이상 넘어갔을 때는 느려지긴 하지만, 성능이 우수한 쿼리이다. 
(다른 방법을 찾아봐야 할듯. 이 주제에서는 생략함)


-- 2020-10-07 SQL Paging Upgrade

-- 조회 관련 태스트
select f1.id, f1.name, f1.price, f2.name as storename from foodmenu_tbl f1, foodstore_tbl f2 where f1.store_id = f2.id
select count(*) from foodmenu_tbl f1, foodstore_tbl f2 where f1.store_id = f2.id


-- Oracle 11 - 자동번호 생성 테이블 정의

-- Table 생성 (FOODMENU_TBL)
-- NEW.ID (Table의 id를 가리킴)
CREATE TABLE foodmenu_tbl
(
    id NUMBER PRIMARY KEY,
    name VARCHAR2(30),
    price NUMBER,
    store_id NUMBER,
    cnt NUMBER,
    regidate DATE
);

-- Sequence 정의
CREATE SEQUENCE foodmenu_sequence
START WITH 1
INCREMENT BY 1;


-- Trigger 생성

-- BEFORE INSERT on '테이블명'
CREATE OR REPLACE TRIGGER foodmenu_trigger
BEFORE INSERT
    ON foodmenu_tbl
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT foodmenu_sequence.nextval INTO :NEW.ID FROM dual;
END;



----------------------------------------------------------------------


-- Table 생성 (FOODSTORE_TBL)
CREATE TABLE foodstore_tbl
(
    id NUMBER PRIMARY KEY,
    name VARCHAR2(30),
    address VARCHAR2(30),
    regidate DATE
);


-- Sequence 정의
CREATE SEQUENCE foodstore_sequence
START WITH 1
INCREMENT BY 1;

-- Trigger 생성
-- BEFORE INSERT on '테이블명'
CREATE OR REPLACE TRIGGER foodstore_trigger
BEFORE INSERT
    ON foodstore_tbl
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT foodstore_sequence.nextval INTO :NEW.ID FROM dual;
END;


-- Sequence 정의
CREATE SEQUENCE foodstore_sequence
START WITH 1
INCREMENT BY 1;

-- Sequence 삭제 //
drop sequence foodname_sequence;

-- Trigger 생성
-- BEFORE INSERT on '테이블명'
-- NEW.ID (Table의 id를 가리킴)
CREATE OR REPLACE TRIGGER foodstore_trigger
BEFORE INSERT
    ON foodstore_tbl
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT foodstore_sequence.nextval INTO :NEW.ID FROM dual;
END;


-- 뷰 생성하기
create or replace view foodmenu_view as
select f1.id, f1.name, f1.price, f2.name as storename from foodmenu_tbl f1, foodstore_tbl f2 where f1.store_id = f2.id order by f1.id desc;


-- 페이징 SQL(뷰 방식)

SELECT * FROM (
SELECT /*+ INDEX_DESC(Z OP_SAMPLE_PK) */ ROWNUM AS RNUM, Z.* FROM (
SELECT * FROM FOODMENU_VIEW
) Z WHERE ROWNUM <= 20
) WHERE RNUM >= 11;



-- 페이징 SQL(뷰 원본으로 작성)
SELECT * FROM (
SELECT /*+ INDEX_DESC(Z OP_SAMPLE_PK) */ ROWNUM AS RNUM, Z.* FROM (
SELECT f1.id, f1.name, f1.price, f2.name as storename from foodmenu_tbl f1, foodstore_tbl f2 where f1.store_id = f2.id order by f1.id desc
) Z WHERE ROWNUM <= 10
) WHERE RNUM >= 1;



-- 특정 쿼리 페이징 SQL(뷰 원본으로 작성)
SELECT * FROM (
SELECT /*+ INDEX_DESC(Z OP_SAMPLE_PK) */ ROWNUM AS RNUM, Z.* FROM (
SELECT f1.id, f1.name, f1.price, f2.name as storename from foodmenu_tbl f1, foodstore_tbl f2 where f1.store_id = f2.id and f1.name like '%무봉리%' order by f1.id desc
) Z WHERE ROWNUM <= 10
) WHERE RNUM >= 1



-- 싱글 쿼리 (페이징)
SELECT * FROM (
SELECT /*+ INDEX_DESC(Z OP_SAMPLE_PK) */ ROWNUM AS RNUM, Z.* FROM (
SELECT * from foodmenu_tbl order by id desc
) Z WHERE ROWNUM <= 10
) WHERE RNUM >= 1



-- 특정 싱글 쿼리 SQL
SELECT * FROM (
SELECT /*+ INDEX_DESC(Z OP_SAMPLE_PK) */ ROWNUM AS RNUM, Z.* FROM (
SELECT * from foodmenu_tbl where name like '%야해해%' order by id desc
) Z WHERE ROWNUM <= 10
) WHERE RNUM >= 1



-- Sample Insert 문
-- 태스트 코드(foodmenu_tbl)
insert into foodmenu_tbl(name, price, store_id, cnt, regidate) values('사슴1', 1500, 1, 0, '2020-01-01');
insert into foodmenu_tbl(name, price, store_id, cnt, regidate) values('사슴2', 1400, 1, 0, '2020-01-01');
insert into foodmenu_tbl(name, price, store_id, cnt, regidate) values('사슴3', 1300, 1, 0, '2020-01-01');
insert into foodmenu_tbl(name, price, store_id, cnt, regidate) values('사슴4', 1100, 1, 0, '2020-01-01');
insert into foodmenu_tbl(name, price, store_id, cnt, regidate) values('사슴5', 1200, 1, 0, '2020-01-01');
insert into foodmenu_tbl(name, price, store_id, cnt, regidate) values('사슴6', 1300, 1, 0, '2020-01-01');
insert into foodmenu_tbl(name, price, store_id, cnt, regidate) values('사슴7', 1500, 1, 0, '2020-01-01');



-- 상점(foodstore_tbl)
-- 태스트 코드
insert into foodstore_tbl(name, address, regidate) values('치즈집1', '천사1', '2020-01-03');
insert into foodstore_tbl(name, address, regidate) values('치즈집2', '천사2', '2020-01-03');
insert into foodstore_tbl(name, address, regidate) values('치즈집3', '천사3', '2020-01-03');
insert into foodstore_tbl(name, address, regidate) values('치즈집4', '천사4', '2020-01-03');
insert into foodstore_tbl(name, address, regidate) values('치즈집5', '천사5', '2020-01-03');
insert into foodstore_tbl(name, address, regidate) values('치즈집6', '천사6', '2020-01-03');


-- 삭제 관련 Delete Query

delete from foodmenu_tbl;
select * from foodmenu_tbl where name = '야해해';


[파일명: food_tbl_sample_boardQueries.sql]


[첨부(Attachments)]

food_tbl_sample_boardQueries.zip




* 맺음글(conclusion)


게시판 페이징 관련해서 학습 또는 자습할 때, 고민을 하면서 쿼리를 날려보고 하면 좋을 듯 싶다.

반응형
728x90
300x250

[Spring-Framework] 40. Connection Pool - xml 정리(Oracle, MySQL, HikariCP, Apache DBCP)


root-context.xml과 같은 context.xml 파일에 빈스(Beans)로 등록해서 사용할 때 매우 요긴하게 사용될 수 있는 DataSource에 대해서 정리하였다.

의외로 정리된 자료를 찾기가 귀찮아서 정리하였으니 참고하여 잘 사용하면 된다.


연관 글은 38, 39번 Spring-Framework에도 적용해볼 수 있다.



1. 정리


많이 필요한 경우가 생긴다. 이거 찾는데 시간 허비하지 말라고 작성한다.


[추가]
     <!-- MySQL - DataSource 셋팅 -->
    <bean id="dataSource2" class="com.mysql.cj.jdbc.MysqlDataSource">
        <property name="URL" value="${ORACLE_DB_URL}" />
        <property name="user" value="${ORACLE_DB_USERNAME}"/>
        <property name="password" value="${ORACLE_DB_PASSWORD}"/>
    </bean>


    <!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> -->
 
     <!-- 2. Oracle - DataSource 셋팅 -->
    <bean id="dataSource" class="oracle.jdbc.pool.OracleDataSource" destroy-method="close">
        <property name="URL" value="${ORACLE_DB_URL}" />
        <property name="user" value="${ORACLE_DB_USERNAME}"/>
        <property name="password" value="${ORACLE_DB_PASSWORD}"/>
        <property name="connectionCachingEnabled" value="true"/>
    </bean>

    <!-- 3. Apache DBCP -->

   <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
         <property name="driverClassName" value="${jdbc.driverClassName}" />
         <property name="url" value="${jdbc.url}" />
         <property name="username" value="${jdbc.username}" />
         <property name="password" value="${jdbc.password}" />
   </bean>


    <!-- 4. HikariCP -->
    <!--
    <bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
         <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
         <property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1521:xe" />
         <property name="username" value="username" />
         <property name="password" value="password" />
    </bean>
 
    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
          <constructor-arg ref="hikariConfig"/>
    </bean>
     -->



    <!-- 5. MariaDB -->
    <!--
    <bean id="dataSource4" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
        <property name="driverClass" value="org.mariadb.jdbc.Driver"/>
        <property name="url" value="jdbc:mariadb://localhost:3306/test"></property>
        <property name="username" value="root"/>
        <property name="password" value="test"/>
    </bean>
     -->
    
    <!-- 6. MyBatis // 트랜젝션 적용 버전 -->
    <!--
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource4"/>
        <property name="configLocation" value="classpath:mybatis/config.xml"/>
        <property name="mapperLocations" value="classpath:mybatis/sql/*.xml"></property>
    </bean>
   
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
   
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource4"/>
    </bean>
   
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
     -->





2. 공식 사이트


http://commons.apache.org/proper/commons-dbcp/

https://github.com/brettwooldridge/HikariCP

-> 메뉴얼이 다소 부족하다. 깔끔하게 잘 정리는 되어 있는데 그런 점이 없는 건 아니다. 


https://www.oracle.com/kr/

https://www.mysql.com/

https://mariadb.org/

반응형
728x90
300x250
[Spring-Framework] 37. Spring-JDBCTemplate - 트랜젝션 (어노테이션, Java - AOP)


이번에 소개할 내용은 지난 36번 글에 이어서 AOP를 추가로 적용한 방법에 대해 소개하려고 한다.

어노테이션 방식의 트랜젝션 구축이 이해가 되지 않으면, 이전 글을 참고하면 도움이 된다.


1. [Spring-Framework] 36. Spring-JDBCTemplate - 트랜젝션 (어노테이션, Java 설정), 2020-10-10

- https://yyman.tistory.com/1461


IDE: Eclipse 2020-06

- Spring Framework 4.2.4 Releases

- Spring-JDBC

- Spring-TX

- Spring-Core

- AspeetJ Weaver

- AspectJ


DB: Oracle Databases 11g (Express Edition)



1. 프로젝트 구성도(비교)


AOP 작업을 추가한 프로젝트와 기존의 프로젝트의 구성을 비교해놓은 것이다.

이 글에서는 이전의 작성된 객체 파일을 그대로 복사 붙여넣기하여 AOP패키지에 적용하였다.


LogAdvisor.java 파일 말고는 추가 코드를 작성하진 않았다.

MainTestAOP.java도 복사, 붙여넣기해서 만든 것이다. 가리키고 있는 명칭만 변경하였다.



 

 



그림 1, 그림 2. 
(Spring 트랜젝션 + Spring JDBC) + AOP

그림 3, 그림 4. 이전 프로젝트
(Spring 트랜젝션 + Spring JDBC)



2. pom.xml 설정하기


((중략) - 추가된 사항)
    
  <!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
  <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${org.springframework-version}</version>
  </dependency>

  
    
  <!-- AspectJ -->
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjrt</artifactId>
   <version>${org.aspectj-version}</version>
  </dependency> 
  

  <!-- AspectJWeaver 추가 -->
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>${org.aspectj-version}</version>
  </dependency>
  





3. LogAdvisor.java(com.website.example.aop)


package com.website.example.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Service;


//관점, 서비스
@Aspect
@Service
public class LogAdvisor {


   // 2단계 - 전 단계 시야)
 
  @Before("execution(* com.website.example.aop.service.AccountServiceAOP.*(..))")
  // @Before("execution(public void sum())")
  // 반환값 없어도 무방
  
   public void logBefore() {
           System.out.println("전 단계");


   }

 
}


파일명: LogAdvisor.java


[첨부(Attachments)]

LogAdvisor.zip



4. AccountServiceAOP.java(com.website.example.aop.service) - 예(복사, 붙여넣기)


코드 자체를 복사 붙여넣기 한 것이다.

AOP를 왜 하는지 철학을 이해하라고 하나 예로 보여주는 것이다.



package com.website.example.aop.service;

import java.sql.SQLException;


import com.website.example.vo.AccountVO;


public interface AccountServiceAOP {

 void accountCreate(AccountVO vo) throws SQLException;
 void accountTransfer(String sender, String receiver, int money) throws SQLException;
 
}




5. AccountServiceImplAOP.java(com.website.example.aop.service) - 예(복사, 붙여넣기)


하나 어노테이션 @Repository를 달아주었다. 그거 말고는 동일하다.



package com.website.example.aop.service;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import com.website.example.dao.AccountDAO;
import com.website.example.dao.AccountDAOImpl;
import com.website.example.vo.AccountVO;


@Repository
@Transactional
public class AccountServiceImplAOP implements AccountServiceAOP{

 private AccountDAO accountDAO;
 private DataSource ds = null;
 
 public AccountServiceImplAOP(DataSource ds) {
  this.accountDAO = new AccountDAOImpl(ds);
  this.ds = ds;
 }

 @Override
 @Transactional(propagation=Propagation.NEVER)
 public void accountCreate(AccountVO vo) throws SQLException {
  accountDAO.createAccount(vo); 
  System.out.println("create CurrentTransactionName: " + TransactionSynchronizationManager.getCurrentTransactionName());
 }
 
 @Override
 @Transactional
 public void accountTransfer(String sender, String receiver, int money) throws SQLException {
  
     int balance = accountDAO.getBalance(sender); // 보내는 사람 잔액 체크
     
        if(balance >= money){ // 보내는 돈이 잔액보다 많으면
     
         System.out.println("transfer CurrentTransactionName: " + TransactionSynchronizationManager.getCurrentTransactionName() );
         
   accountDAO.minus(sender, money);
   accountDAO.plus(receiver, money);
   
        } else{

         System.out.println("돈 없음");
         //throw new NoMoneyException();
        }
  
 }


}




6. RootConfigAOP.java(com.website.example.common) - 예(복사, 붙여넣기)



package com.website.example.common;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
@Import({DBConfigAOP.class})
@ComponentScan(basePackages = {"com.website.example"})
@EnableAspectJAutoProxy
//@ComponentScan(basePackages = {"com.local.example.beans", "com.local.example.advisor"})
public class RootConfigAOP {


}




7. DBConfigAOP.java(com.website.example.common) - 예(복사, 붙여넣기)


package com.website.example.common;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.website.example.dao.AccountDAO;
import com.website.example.dao.AccountDAOImpl;
import com.website.example.aop.service.AccountServiceAOP;
import com.website.example.aop.service.AccountServiceImplAOP;


@Configuration
@EnableTransactionManagement
public class DBConfigAOP {

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
   
    @Bean
    public DataSource dataSource() {
     
     DataSource dataSource = new MyDataSourceFactory().getOracleDataSource();
     
     /* Apache DBCP
     
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/testDb?useUnicode=true&characterEncoding=utf8");
        dataSource.setUsername("test");
        dataSource.setPassword("test123!@#");
       
        */
        return dataSource;
    }
   
    @Bean
    public AccountDAO accountDAOImpl() {
     
     AccountDAO dao = new AccountDAOImpl(dataSource());
     return dao;
    }
   
    @Bean
    public AccountServiceAOP accountServiceImplAOP() {
     
     AccountServiceAOP service = new AccountServiceImplAOP(dataSource());

     return service;
    }
   
}




8. MainTestAOP.java(com.website.example.unit) - 예(복사, 붙여넣기)



package com.website.example.unit;


import java.sql.SQLException;

import java.sql.Timestamp;

import javax.sql.DataSource;

import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.website.example.common.MyDataSourceFactory;
import com.website.example.common.RootConfigAOP;

import com.website.example.dao.AccountDAOImpl;
import com.website.example.aop.service.AccountServiceAOP;
import com.website.example.aop.service.AccountServiceImplAOP;

import com.website.example.vo.AccountVO;


class MainTestAOP {

 @Test
 void test() throws SQLException {
  
       @SuppressWarnings("resource")
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RootConfigAOP.class);

  
        AccountServiceAOP service = (AccountServiceAOP) applicationContext.getBean("accountServiceImplAOP");
  
  AccountVO vo = new AccountVO();
  
  // 1. 계정 생성
  vo.setName("홍길동2");
  vo.setBalance(10000);
  vo.setRegidate(Timestamp.valueOf("2020-01-20 11:05:20"));
  service.accountCreate(vo);
  
  // 2. 계정 생성
  vo.setName("홍길자2");
  vo.setBalance(0);
  vo.setRegidate(Timestamp.valueOf("2020-01-20 23:05:20"));
  service.accountCreate(vo);
  
  // 3. 거래 처리
  service.accountTransfer("홍길동", "홍길자", 500);
  
  
 }


}




* 맺음글(Conclusion)


AOP를 쉽고 간단하게 트랜젝션과 연결해서 사용하는 방법에 대해서 소개하였다.



반응형
728x90
300x250
[Spring-Framework] 34. Spring JDBCTemplate - 사용하기


사용 방법은 무척 간단하다.

pom.xml 등의 학습 지식이 없다면, 이전 글을 참고하면 좋겠다.


[작성 환경]

- IDE: Eclipse 2020-06
- Spring Framework 4.2.4 RELEASES




1. POM.xml 설정


https://mvnrepository.com/artifact/org.springframework/spring-jdbc

사이트에 접속해서 Spring-JDBC의 정보를 찾아서 pom.xml에 입력시켜 준다.

(중략)
  
  <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
  <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${org.springframework-version}</version>
  </dependency>


(중략)




2. 구현 부분

 

핵심만 밑줄로 표기하였다.



package com.website.example.board;

import java.sql.Date;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import com.website.example.common.MyDataSourceFactory;
import com.website.example.model.FoodMenuVO;
import com.website.example.model.FoodMenuViewVO;


public class BoardDAOSpring {
 
 // Spring Framework - JDBC
 private JdbcTemplate jdbcTemplate = null;
 
 private final String FOODMENU_INSERT = "insert into foodmenu_tbl(name, price, store_id, cnt, regidate) values(?, ?, ?, ?, ?)";
 private final String BOARD_UPDATE = "update board set subject=?, memo=? where id=?";
 private final String FOODMENU_DELETE = "delete foodmenu_tbl where id=?";
 private final String BOARD_GET = "select * from board where id=?";

 
 private final String BOARD_LIST = "SELECT * FROM ( " +
                "SELECT /*+ INDEX_DESC(Z OP_SAMPLE_PK) */ ROWNUM AS RNUM, Z.* FROM ( " +
                   "SELECT f1.id, f1.name, f1.price, f2.name as storename from foodmenu_tbl f1, foodstore_tbl f2 " +
                "where f1.store_id = f2.id order by f1.id desc " +
                ") Z WHERE ROWNUM <= ? " +
                ") WHERE RNUM >= ?";
 
      private final String BOARD_FULL_COUNT = "select count(*) from foodmenu_tbl f1, foodstore_tbl f2 where f1.store_id = f2.id";

      // 변형을 할 필요가 있음. (태스트용)

      public BoardDAOSpring() {

              MyDataSourceFactory sourceFactory = new MyDataSourceFactory();
              DataSource ds = sourceFactory.getOracleDataSource();

              this.jdbcTemplate = new JdbcTemplate(ds);
      }
 
      public List<FoodMenuViewVO> getList(){
 
          // 코드 간결하게 작성가능해짐.
          System.out.println("Spring JDBC - GetBoardList()");
         //return jdbcTemplate.query(BOARD_LIST, new BoardRowMapper());
  
          Object args[] = {10, 1};
  
          return jdbcTemplate.query(BOARD_LIST, args, new FoodMenuViewRowMapper());

       // return null;

     }
 
     public int getCount() {
  
          int result = 0;
          FoodMenuVO vo =  jdbcTemplate.queryForObject(BOARD_FULL_COUNT, new FoodMenuViewCntRowMapper());
          result = vo.getId();
  
          System.out.println("갯수:" + result);
  
         return result;
  
     }
 
      public void insertTest() {
  
          FoodMenuVO vo = new FoodMenuVO();

  
          // 약 10만 개
          // insert 후에 commit 할 것
          /*

          for(int j = 0; j < 2000 ; j++) {
                for ( int i = 0; i < 50; i++) {
   
                       vo.setName("하하하하1234" + i);
                       vo.setPrice(1000);
                       vo.setStore_id(1);
                       vo.setCnt(0);
                       vo.setRegidate(Date.valueOf("2020-01-03"));
                       vo.setStore_id(1);
    
                       jdbcTemplate.update(FOODMENU_INSERT, vo.getName(),
                              vo.getPrice(), vo.getStore_id(),
                              vo.getCnt(), vo.getRegidate());
    
                }
        }// end of if
  */
  
        vo.setName("야해해");
        vo.setPrice(1000);
        vo.setStore_id(1);
        vo.setCnt(0);
        vo.setRegidate(Date.valueOf("2020-01-05"));
        vo.setStore_id(1);
  
        Object[] args = {vo.getName(),
                 vo.getPrice(),
                 vo.getStore_id(),
                 vo.getCnt(),
                 vo.getRegidate()};

  
        jdbcTemplate.update(FOODMENU_INSERT, args);
  
 }
 

      // 글 삭제
      public void deleteFoodMenu(FoodMenuVO vo) {
  
             System.out.println("===> Spring JDBC로 deleteBoard() 기능 처리");
             jdbcTemplate.update( FOODMENU_DELETE, vo.getId() );
  
      }
 
 
}


파일명: BoardDAOSpring.java



3. Mapper 구현


Spring JDBC의 특징은 Mapper 영역을 구현할 수 있다는 점이다.


package com.website.example.board;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

import com.website.example.model.FoodMenuVO;
import com.website.example.model.FoodMenuViewVO;


public class FoodMenuRowMapper implements RowMapper<FoodMenuVO>  {


        @Override
        public FoodMenuVO mapRow(ResultSet rs, int rowNum) throws SQLException {
  
              FoodMenuVO vo = new FoodMenuVO();

              vo.setId(rs.getInt(1));
              vo.setName(rs.getString(2));
              vo.setPrice(rs.getInt(3));
              vo.setStore_id(rs.getInt(4));
              vo.setCnt(rs.getInt(5));
              vo.setRegidate(rs.getDate(6));
  
              return vo;
        }

}


파일명: FoodMenuRowMapper.java


package com.website.example.board;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

import com.website.example.model.FoodMenuVO;


public class FoodMenuViewCntRowMapper implements RowMapper<FoodMenuVO>  {


        @Override
        public FoodMenuVO mapRow(ResultSet rs, int rowNum) throws SQLException {
  
               FoodMenuVO vo = new FoodMenuVO();
               vo.setId(rs.getInt(1));
               // System.out.println(rs.getInt(1));
  
               return vo;
       }

}


파일명: FoodMenuViewCntRowMapper.java


Mapper를 두 가지 형태로 두었는데, 상황에 따라서 변형이 가능하다.



* 맺음글(Conclusion)


Spring JDBC에 대해서 살펴보았다.

짧게 적은 이유는 사용 방법이 간단해서 그렇다. Spring JDBC의 트랜젝션을 언급하기 위해서 그렇다.


1. [Spring-Framework] 35. Spring-JDBCTemplate - 트랜젝션 (어노테이션 X), 2020-10-09

- https://yyman.tistory.com/1460


반응형
728x90
300x250
[PC 활용] 오라클 - 반복문으로 다중 입력 시(예:insert into) 퍼짐 현상


이 글은 JSP, Java, PHP, C#를 예로 들어서 개발을 수행했을 때, 태스트로 "DML(Insert 문)"으로 DB에 Query 호출을 반복문으로 하였을 때 초기 환경 상태에서의 오라클 데이터베이스에서 일어나는 반응에 대한 글이다.


[PseudoCode]


public void insertMember(){


    (연결자)
    Connection conn = .....;
    
    try{
        conn.open(); // (연결)

        for ( 1 to 10000 ){


           String sql = "insert into ........";

           conn.executeQuery(sql);

        }


     }catch(Exception e){

     }finally{
           conn.close(); // (종료)
     }

    


}


슈도 코드) 문제가 되는 영역


이런 문제를 처리하고자 했을 때, 발생되는 오류를 소개하려고 한다.


[태스트 환경]

1. MS Windows 10

2. Oracle 11g (Express Edition, XE)



1. 프로그래밍 도구에서 호출되는 오류명


ora-12519 TNS:no appropriate handler found


이러한 오류가 발생한다면, 다음과 같이 입력하여 해결하면 된다.



2. 해결 방법


명령어를 몇 가지 요약하였다.


1. 명령 프롬프트 실행하기

   cmd


2. 관리자 계정 접속 명령어

   sqlplus "/as sysdba"


3. 프로세스 조회하기
   SQL> SELECT * FROM v$resource_limit where resource_name='processes';


4. 프로세스 조정하기 크기
   SQL> ALTER SYSTEM SET PROCESSES=500 SCOPE=spfile; -- 200으로 늘려줌


5. 서버 재시작
   SQL> shutdown immediate; --셧다운
   SQL> startup; --재시작



그림 1. cmd 명령어 - 시작 메뉴


시작 메뉴에서 cmd를 입력한다.



그림 2. sqlplus


sqlplus "/as sysdba"


이렇게 입력해도 관리자 권한으로 오라클에 접속할 수 있다.


SQL> SELECT * FROM v$resource_limit where resource_name='processes';


프로세스 조회 명령어로 프로세스 자원 한계를 확인하자.

프로세스가 100으로 작게 설정되어 있으니 조금 더 늘려보도록 하자.



그림 3. sqlplus


SQL> ALTER SYSTEM SET PROCESSES=500 SCOPE=spfile;


프로세스 500으로 수정을 해주었다.

변경되었는지 조회를 해보자.


SQL> SELECT * FROM v$resource_limit where resource_name='processes';


조회를 해보니, 변경되지 않았다.

그래서 commit을 안 해서 발생한 문제인지 commit을 입력하였다.


SQL> commit;


그리고 다시 조회를 해본다.


SQL> SELECT * FROM v$resource_limit where resource_name='processes';


변경되지 않았다.

오라클 DB를 재부팅해보면 해결되는 문제인지 시도해보았다.



그림 4. sqlplus


SQL> shutdown immediate;

SQL> startup;


서버를 다시 시작하였다.

그리고 프로세스 조회 명령어를 입력하였다.


SQL> SELECT * FROM v$resource_limit where resource_name='processes';


변경된 프로세스 내용을 확인할 수 있다.

반응형
728x90
300x250
[PC 활용] 오라클 XE 11(Oracle Express Edition 11g) - 설치 및 로그인


윈도우 10에서 오라클 XE를 설치하는 방법에 대해서 소개하려고 한다.

Standard Edition, Enterprise Edition에 비해서 설치 프로그램 용량도 적고, 간편하다.

다만, 아파치 톰캣을 사용하는 개발자라면, 포트 충돌이 날 수 있다.



[작성 환경]

1. MS윈도우 10

2. 포멧 후 깨끗한 상태



1. 설치하기


설치하는 방법은 다음과 같다.




그림 1. 오라클 설치하기


setup.exe를 실행한다.



그림 2. 오라클 설치하기


잠시 기다린다.



그림 3. 오라클 설치하기


Next를 누른다.



그림 4. 오라클 설치하기


"I accpet the terms......"을 체크한다.

Next를 누른다.




그림 5. 오라클 설치하기


Next를 누른다.



그림 6. 오라클 설치하기


이 비밀번호는 Oracle XE에서는 매우 중요한 암호가 된다. 기억해두자.

암호를 입력한 후에는 Next를 누른다.




그림 7. 오라클 설치하기


설치 환경설정을 살펴본다.

Oracle HTTP에서도 8080포트를 사용하는 것을 살펴볼 수 있다.


Install을 누른다.




그림 8. 오라클 설치하기


기다린다.



그림 9. 오라클 설치하기


Finish를 누르면 설치가 완료된 것이다.




2. 명령 프롬프트(cmd) 작업 - Sqlplus


설치를 완료하였다면, 바로 사용가능한 것이 아니라 몇 가지 계정 설정을 해줘야 한다.



그림 10. sqlplus 작업을 위한 명령 프롬프트 실행하기


시작 메뉴에서 cmd를 입력한다.



그림 11. sqlplus 작업을 위한 명령 프롬프트 실행하기


system이라고 입력한다.

아까 입력했던 패스워드를 입력한 후 엔터를 누른다.



그림 12. sqlplus 작업을 위한 명령 프롬프트 실행하기


경로: C:\oraclexe\app\oracle\product\11.2.0\server\rdbms\admin\
찾아야 하는 파일명: scott.sql


해당 파일을 찾아야 한다.



그림 13. sqlplus 작업을 위한 명령 프롬프트 실행하기


SQL> @C:\oraclexe\app\oracle\product\11.2.0\server\rdbms\admin\scott.sql
SQL> alter user scott identified by tiger;

SQL> commit;


3줄 명령어를 입력한다. (SQL>은 입력하는 게 아님)


scott 계정을 활성화한 것이다.



4. scott 계정 접속하기


앞서 작업한 scott 계정을 로그인해볼 것이다.



그림 14. sqlplus 작업을 위한 명령 프롬프트 실행하기


SQL> conn scott/tiger
SQL> show user;

SQL> select * from tab;


3줄 명령어는 다음과 같다.

1줄은 scott/tiger(비밀번호)로 연결하라는 것이다.

2줄은 접속중인 계정이 무엇인지 확인하는 명령어이다.

3줄은 tab 테이블을 조회하는 명령이다. (SELECT문)



그림 15. sqlplus 작업을 위한 명령 프롬프트 실행하기


SQL> exit


exit는 연결을 끊는다는 명령어이다.


c:\Users\{사용자계정명}>sqlplus
Enter user-name: system
Enter password: (비밀번호 입력하기)


관리자 계정으로 접속하는 명령어를 3줄로 요약한 것이다. 
(xe버전 방법은 standard, enterprise의 방법은 미세한 차이가 있을 수도 있음.)



5. HR 계정 - 활성화 하기


Oracle XE 11g에서 제공하는 연습용 계정 HR을 활성화 하는 방법이다.


SQL> alter user hr account unlock;



그림 16. sqlplus 작업을 위한 명령 프롬프트 실행하기


간단한 명령으로 연습용 HR 계정을 활성화 시킬 수 있다.


- 참고로 Oracle 공식 사이트에서 제공하는 메뉴얼도 있으니 참고하면 좋겠다.

https://docs.oracle.com/cd/E17781_01/admin.112/e18585/toc.htm#XEGSG120

반응형

+ Recent posts