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] 35. Spring-JDBCTemplate - 트랜젝션 (어노테이션 X)


@Transactional이라는 어노테이션 기능 방식이 아닌 직접 구현으로 트랜젝션을 Spring JDBC에서 다루는 방법을 소개하려고 한다.


아마 이전의 Spring Framework의 34번 글을 실습해보았다면, 희안하게 insert 명령은 수행되어서 SQL Developer로 SELECT문으로 출력했을 때는 화면에 보이는데, Java에서 DB 질의로 SELECT문으로 조회한다면 동작이 되는 경우도 있고, 안 된 경우도 있었을 거 같다.


이 문제는 commit()을 했느냐 안 했느냐의 차이에서 발생한다.


1. [Spring-Framework] 34. Spring JDBCTemplate - 사용하기, 2020. 10. 9. 21:01

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


이 글의 개선 버전이라고 보면 되겠다.


- IDE: Eclipse 2020-06

- Spring Framework 4.2.4 RELEASES



1. 프로젝트 구성


다음 그림은 프로젝트를 구성하고 있는 구성도이다.



그림 1. 프로젝트 구성도


이전 글에서는 프로젝트 구성도까진 필요할까 고민했는데, 심플하게 적어주는 것이 더 나아보여서 핵심만 적었다.

지금 글에서는 Spring JDBC에서 트랜젝션을 제대로 사용하는 방법에 대해서 소개하려고 한다.



2. Project Factes, Build Paths, Java Compiler 버전 변경해주기


* Build Paths -> JRE Library 1.8로 변경, Junit 5 추가할 것

* Java Compiler -> Compiler complicance Level: 1.8 환경으로 바꿔줄 것

* Project Factes -> Java 버전 1.8로 변경할 것


- 오라클 JDBC는 WEB-INF/lib에 넣어서 해결해주면 된다.

  (Build Paths에 등록도 해줘야 함.)




3. pom.xml 설정하기



 <properties>
  <java-version>1.8</java-version>
  <org.springframework-version>4.2.4.RELEASE</org.springframework-version>
  <org.aspectj-version>1.6.10</org.aspectj-version>
  <org.slf4j-version>1.6.6</org.slf4j-version>
 </properties>
 <dependencies>


(중략)


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

(중략)



  <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
  <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.21</version>
  </dependency>





4. AccountTbl.sql


-- Transaction 실습 DB (은행 - Account)
-- Oracle 11 - 자동번호 생성 테이블 정의
-- Table 생성 (FOODMENU_TBL)
-- NEW.ID (Table의 id를 가리킴)
CREATE TABLE account_tbl
(
    idx NUMBER PRIMARY KEY,
    name VARCHAR2(30),
    balance NUMBER,
    regidate TIMESTAMP
);

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

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


파일명: AccountTbl.sql


[첨부(Attachments)]

AccountTbl.zip



5. MyDataSourceFactory.java (com.website.example.common)


package com.website.example.common;

import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import com.mysql.cj.jdbc.MysqlDataSource;

import oracle.jdbc.pool.OracleDataSource;

public class MyDataSourceFactory {
 
 private InputStream is = null;
    private Properties props = null;
 
 public MyDataSourceFactory()  {
  
        String resource = "db.properties";
        this.is = getClass().getClassLoader().getResourceAsStream(resource);
        this.props = new Properties();
       
 }
 
 public DataSource getMySQLDataSource() {
  
        MysqlDataSource mysqlDS = null;
       
        try {
         
            props.load(is);
            mysqlDS = new MysqlDataSource();
            mysqlDS.setURL(props.getProperty("MYSQL_DB_URL"));
            mysqlDS.setUser(props.getProperty("MYSQL_DB_USERNAME"));
            mysqlDS.setPassword(props.getProperty("MYSQL_DB_PASSWORD"));
           
        } catch (IOException e) {
            e.printStackTrace();
        }
       
        return mysqlDS;
       
    }
    
    public DataSource getOracleDataSource(){
     
        OracleDataSource oracleDS = null;
       
        try {
         
            props.load(is);
            oracleDS = new OracleDataSource();
            oracleDS.setURL(props.getProperty("ORACLE_DB_URL"));
            oracleDS.setUser(props.getProperty("ORACLE_DB_USERNAME"));
            oracleDS.setPassword(props.getProperty("ORACLE_DB_PASSWORD"));
           
           
        } catch (IOException e) {
         
            e.printStackTrace();
           
        } catch (SQLException e) {
         
            e.printStackTrace();
           
        }
       
        return oracleDS;
       
    }

}


파일명: MyDataSourceFactory.java


[첨부(Attachments)]

MyDataSourceFactory.zip


AccountTbl.zip



6. AccountVO.java (com.website.example.vo)


package com.website.example.vo;

import java.sql.Timestamp;;


public class AccountVO {
 
     private int idx;
     private String name;
     private int balance;
     private Timestamp regidate;
 
     public int getIdx() {
          return idx;
     }
 
     public void setIdx(int idx) {
          this.idx = idx;
     }
 
     public String getName() {
          return name;
     }
 
     public void setName(String name) {
          this.name = name;
     }
 
     public int getBalance() {
          return balance;
     }

 

     public void setBalance(int balance) {
          this.balance = balance;
     }


     public Timestamp getRegidate() {
          return regidate;
     }
 
     public void setRegidate(Timestamp regidate) {
          this.regidate = regidate;
      }
   
}


파일명: AccountVO.java


[첨부(Attachments)]

AccountVO.zip

AccountTbl.zip



7. AccountDAO.java (com.website.example.dao)


package com.website.example.dao;


import java.sql.SQLException;

import com.website.example.vo.AccountVO;


public interface AccountDAO {
 
       void createAccount(AccountVO vo) throws SQLException;
       int getBalance(String name);
       void minus(String name, int money) throws SQLException;
       void plus(String name, int money) throws SQLException;
 
}


파일명: AccountDAO.java


[첨부(Attachments)]

AccountDAO.zip



8. AccountDAOImpl.java (com.website.example.dao)


package com.website.example.dao;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

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

import com.website.example.common.MyDataSourceFactory;
import com.website.example.vo.AccountVO;

public class AccountDAOImpl implements AccountDAO {


       // Spring Framework - JDBC
       private JdbcTemplate jdbcTemplate = null;
       private DataSource ds = null;
 
       private final String INSERT = "insert into account_tbl(name, balance, regidate) values(?, ?, ?)";
       private final String SELECT_BALANCE = "select * from account_tbl where name = ?";
       private final String UPDATE_MINUS = "update account_tbl set balance = (select balance from account_tbl where name = ?) - ? " +
          " where name = ?";
       private final String UPDATE_PLUS = "update account_tbl set balance = (select balance from account_tbl where name = ?) + ? " +
          " where name = ?";
 
 
        public AccountDAOImpl(DataSource ds) {
               this.jdbcTemplate = new JdbcTemplate(ds);
               this.ds = ds;
        }
 
         public void createAccount(AccountVO vo) throws SQLException {
  
                TransactionSynchronizationManager.initSynchronization(); // 동기화
                Connection c = DataSourceUtils.getConnection(ds); //커넥션을 생성

  
                // 데이터 저장소 바인딩(트랜젝션)
                c.setAutoCommit(false); //트랜잭션을 시작
  
                try{
   
                    this.jdbcTemplate.update(INSERT, vo.getName(), vo.getBalance(), vo.getRegidate());
                    c.commit(); // 커밋
       
               }
               catch(Exception e) {
                    c.rollback(); // 예외가 발생하면 롤백
       
               }finally {
      
                    DataSourceUtils.releaseConnection(c, ds); // 스프링 유틸리티를 이용해 DB커넥션을 닫는다.
                    TransactionSynchronizationManager.unbindResource(ds); // 동기화 작업을 종료하고
                    TransactionSynchronizationManager.clearSynchronization();     // 정리한다.
      
               }

  
 }
 
    public int getBalance(String name){
     
     Object[] args = {name};
     
     AccountVO vo = this.jdbcTemplate.queryForObject(SELECT_BALANCE, args, new AccountRowMapper());
       
        int result = vo.getBalance();
     
        return result;
    }
    
    public void minus(String name, int money) throws SQLException{

     // 예외 발생시키기
     if(true){
      throw new SQLException(); // 의도적 예외 발생
        }
     
        this.jdbcTemplate.update(UPDATE_MINUS, name, money, name);
       
    }
    
    public void plus(String name, int money) throws SQLException{
          
        this.jdbcTemplate.update(UPDATE_PLUS, name, money, name);
    }
 
}


파일명: AccountDAOImpl.java


[첨부(Attachments)]

AccountDAOImpl.zip


AccountDAO.zip



9. AccountRowMapper.java (com.website.example.dao)


package com.website.example.dao;


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

import org.springframework.jdbc.core.RowMapper;

import com.website.example.vo.AccountVO;


public class AccountRowMapper implements RowMapper<AccountVO>  {


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

                 vo.setIdx(rs.getInt(1));
                 vo.setName(rs.getString(2));
                 vo.setBalance(rs.getInt(3));
                 vo.setRegidate(rs.getTimestamp(4));
  
                 return vo;
        }

}


파일명: AccountRowMapper.java


[첨부(Attachments)]

AccountRowMapper.zip




10. AccountService.java (com.website.example.service)


package com.website.example.service;

import java.sql.SQLException;

import com.website.example.vo.AccountVO;


public interface AccountService {

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


파일명: AccountService.java


[첨부(Attachments)]

AccountService.zip




11. AccountServiceImpl.java (com.website.example.service)


package com.website.example.service;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;

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



public class AccountServiceImpl implements AccountService{

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


      @Override
      public void accountCreate(AccountVO vo) throws SQLException {
            this.accountDAO.createAccount(vo);  
      }
 

      @Override
      public void accountTransfer(String sender, String receiver, int money) throws SQLException {
  
               int balance = accountDAO.getBalance(sender); // 보내는 사람 잔액 체크
               Connection conn = null;
     
               if(balance >= money){ // 보내는 돈이 잔액보다 많으면

                    TransactionSynchronizationManager.initSynchronization(); // 트랜잭션 동기화 작업 초기화

                    conn = ds.getConnection();
                    conn.setAutoCommit(false);
         
                    try {
                       accountDAO.minus(sender, money);
                       accountDAO.plus(receiver, money);
          
                       conn.commit(); // 성공
    
                   }catch(SQLException e) {
                        conn.rollback(); // 실패
                   }
                   finally {
                        // 커넥션 종료(Spring)
                       DataSourceUtils.releaseConnection(conn, this.ds);
          
                       // 동기화 작업을 종료하고 저장소를 비운다
                       TransactionSynchronizationManager.clearSynchronization();
                   }
           
             } else{

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


}


파일명: AccountServiceImpl.java


[첨부(Attachments)]

AccountServiceImpl.zip



12. MainTest.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 com.website.example.common.MyDataSourceFactory;
import com.website.example.service.AccountService;
import com.website.example.service.AccountServiceImpl;
import com.website.example.vo.AccountVO;


class MainTest {


     @Test
     void test() throws SQLException {

            MyDataSourceFactory sourceFactory = new MyDataSourceFactory();
            DataSource ds = sourceFactory.getOracleDataSource();
  
            AccountService service = new AccountServiceImpl(ds);
            AccountVO vo = new AccountVO();
   
            /*
            // 1. 계정 생성
           vo.setName("홍길동");
           vo.setBalance(10000);
           vo.setRegidate(Timestamp.valueOf("2020-01-20 10:05:20"));
           service.accountCreate(vo);
  
           // 2. 계정 생성
           vo.setName("홍길자");
           vo.setBalance(0);
           vo.setRegidate(Timestamp.valueOf("2020-01-20 22:05:20"));
           service.accountCreate(vo);
            */
  
           // 3. 거래 처리
           service.accountTransfer("홍길동", "홍길자", 500);
  
  
      }

}


파일명: MainTest.java


[첨부(Attachments)]

MainTest.zip

AccountServiceImpl.zip




* 맺음글(Conclusion)


스프링 JDBC에서 트랜젝션 사용하는 방법에 대해서 어노테이션을 적용하지 않는 방법에 대해서 살펴보았다.


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

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

(이해를 돕기 위하여 실제 개발환경도구에서 트랜젝션 시연 영상도 포함해서 소개하였다.)


반응형
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


반응형

+ Recent posts