XML 선언

이 부분은 데이터베이스의 종류, 사용하는 플랫폼에 따라 달라지므로 사용 환경에 맞는지 확인해봐야 합니다. Spring Boot의 마리아DB(mariadb)기준은 이 글을 참고해주세요.

이 부분은 properties 태그와 dependencies 태그 사이에 추가합니다.

<repositories>
  <repository>
    <id>oracle</id>
    <url>http://maven.jahia.org/maven2</url>
  </repository>
</repositories>

이 부분은 dependencies 태그 쌍 내에 추가합니다.

        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>12.1.0.2</version>
        </dependency>
 
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>
 
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>

아래 내용은 context.xml에 추가합니다.

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
        <property name="url" value="[DB주소: jdbc:oracle:thin:@localhost:1521:xe]"></property>
        <property name="username" value="[아이디]" />
        <property name="password" value="[비밀번호]" />
    </bean>
    
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>

 

JDBC

먼저 비교를 위해 일반적인 방식(JDBC) 으로 클래스 파일을 작성해 보겠습니다.

DAO
@Repository
public class MessagesDAOImpl implements MessagesDAO {
 
    public MessagesDAOImpl() {
 
    }
 
    @Override
    public int insert(Connection con, Message msg) throws SQLException, NamingException {
        String sql = "insert into messages values(msg_seq.nextval, ?, ?)";
 
        PreparedStatement ps = con.prepareStatement(sql);
 
        ps.setString(1, msg.getWriter());
        ps.setString(2, msg.getMessage());
 
        int result = ps.executeUpdate();            
 
        return result;        
 
    }
 
    @Override
    public List<Message> getAllData(Connection con) throws SQLException, NamingException {
        String sql = "select * from messages";
        PreparedStatement ps = con.prepareStatement(sql);
        ResultSet rs = ps.executeQuery();
 
        List<Message> list = new ArrayList<>();
        while(rs.next()) {
            Message msg = new Message();
            msg.setSeq(rs.getInt(1));
            msg.setWriter(rs.getString(2));
            msg.setMessage(rs.getString(3));
            list.add(msg);
        }
 
        return list;
    }
 
    @Override
    public int update(Connection con, Message msg) throws SQLException, NamingException {
        String sql = "update messages set writer = ?, message = ? where seq = ?";
 
        PreparedStatement ps = con.prepareStatement(sql);
        
        ps.setString(1, msg.getWriter());
        ps.setString(2, msg.getMessage());
        ps.setInt(3, msg.getSeq());    
        
        return ps.executeUpdate();
    }
 
    @Override
    public int delete(Connection con, int seq) throws SQLException, NamingException {
        String sql = "delete from messages where seq = ?";
        
        PreparedStatement ps = con.prepareStatement(sql);
        
        ps.setInt(1, seq);
        
        return ps.executeUpdate();        
    }
 
}

 

Service
 
@Service 
public class MessagesServiceImpl implements MessagesService {
    
    @Autowired
    private MessagesDAO mdao;
    
    @Autowired
    private DataSource ds;
    
    @Override
    public int insert(Message msg) throws SQLException, NamingException {
        
        Connection con = ds.getConnection();
        try {
            int result = this.mdao.insert(con, msg);
            return result;
        } catch (SQLException e) {
            con.rollback();
            throw e;
        } finally {
            con.commit();
            con.close();
        }
 
    }
    
    @Override
    public List<Message> getAllData() throws SQLException, NamingException {
        Connection con = ds.getConnection();
        List<Message> list = this.mdao.getAllData(con);
        con.close();
        return list;
    }
    
    @Override
    public int update(Message msg) throws SQLException, NamingException {
        Connection con = ds.getConnection();
        try {
            int result = mdao.update(con, msg);
            return result;
        } catch(SQLException | NamingException e) {
            con.rollback();
            throw e;
        } finally {
            con.commit();
            con.close();
        }
        
    }
    
    @Override
    public int delete(int seq) throws SQLException, NamingException {
        Connection con = ds.getConnection();
        
        try {
            int result = this.mdao.delete(con, seq);
 
            return result;
            
        } catch (SQLException e) {
            con.rollback();
            throw e;
        } finally {
            con.commit();
            con.close();
        }
    }
}

 

메인 클래스
public static void main(String[] args) {
 
        AbstractApplicationContext x = new GenericXmlApplicationContext("appContext.xml");
 
        // 컨트롤러와 맞대응하는 것은 서비스 레이어 -> ServiceImpl
        MessagesService ms = (MessagesServiceImpl) x.getBean("messagesServiceImpl");
 
        try {
            List<Message> list = ms.getAllData();
            for(Message msg : list) {
                System.out.println(msg);
            }
        } catch (SQLException | NamingException e) {
            e.printStackTrace();
        }
 
        x.close();
    }

 

스프링 JDBC

Spring JDBC는 JDBC를 더 쉽게 쓸 수 있도록 가공한 것으로, update 관련 기능은 sql문을 변수로 저장하는 것만으로 실행할 수 있어 일반 JDBC보다 간편하게 이용할 수 있습니다. JDBC의 일종이기 때문에 SQL안에 ?가 들어간다든가(PreparedStatement) RowMapper 클래스를 이용하는 것 등 많은 점이 기존 JDBC와 유사합니다.

 

DAO
@Repository
public class MessagesDAOImpl implements MessagesDAO {
    
    @Autowired
    private JdbcTemplate template;
 
    public MessagesDAOImpl() {
 
    }
 
    @Override
    public int insert(Message msg) throws SQLException, NamingException {
        
        String sql = "insert into messages values(msg_seq.nextval, ?, ?)";
        return template.update(sql, msg.getWriter(), msg.getMessage());
    }
 
    @Override
    public List<Message> getAllData() throws SQLException, NamingException {
        
        String sql = "select * from messages";
        
        return template.query(sql, (rs, rowNum) -> {
            Message msg = new Message();
            msg.setSeq(rs.getInt(1));
            msg.setWriter(rs.getString(2));
            msg.setMessage(rs.getString(3));
            return msg;
        });
        
        /* 람다식의 원래 형태:
         * new RowMapper<X>() {
         *     @Override
         *     public X mapRow(ResultSet rs, int rowNum) throws SQLException {... return X;}
         */
        
    }
 
    @Override
    public int update(Message msg) throws SQLException, NamingException {
        
        String sql = "update messages set writer = ?, message = ? where seq = ?";
        return template.update(sql, msg.getWriter(), msg.getMessage(), msg.getSeq());
    }
 
    @Override
    public int delete(int seq) throws SQLException, NamingException {
        
        String sql = "delete from messages where seq = ?";
        return template.update(sql, seq);
    }
 
}

 

Service
@Service
public class MessagesServiceImpl implements MessagesService {
    
    @Autowired
    private MessagesDAO mdao;
    
    @Override
    public int insert(Message msg) throws SQLException, NamingException {
        return mdao.insert(msg);
    }
    
    @Override
    public List<Message> getAllData() throws SQLException, NamingException {
        return mdao.getAllData();
    }
    
    @Override
    public int update(Message msg) throws SQLException, NamingException {
        return mdao.update(msg);
    }
    
    @Override
    public int delete(int seq) throws SQLException, NamingException {
        return mdao.delete(seq);
    }
}

 

위의 DAO에서 getAllData 메소드는 List 형식으로 자료를 가져옵니다. 보통 select 결과는 여러 개이므로 상관 없을수도 있지만, 단일 행의 결과가 예상될 때도 있습니다. 그럴 때엔 queryForObject를 사용합니다.

@Override
public Message selectBySeq(int seq) {
    
    String sql = "select * from messages where seq=?";
    return template.queryForObject(sql, new Object[] {seq}, (rs, rowNum) -> {
        
        Message msg = new Message();
        msg.setSeq(rs.getInt(1));
        msg.setWriter(rs.getString(2));
        msg.setMessage(rs.getString(3));
        
        return msg;
    });
}

 

트랜잭션 처리

쿼리 중 트랜잭션 처리가 필요할 수도 있습니다. 트랜잭션 처리(annotation-driven 형식)가 필요하다면 아래 부분을 context.xml에 추가하고 네임스페이스 선언도 tx를 인식할 수 있도록 수정합니다.

namespace 부분에 xmlns:tx="http://www.springframework.org/schema/tx" 추가

<!-- 데이터베이스 트랜잭션: commit, rollback -->
<bean id="transactionManager" 
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- 트랜잭션 지정 방식으로 annotation 선택 -->
<tx:annotation-driven/>

사용 방법은 서비스(@Service) 레이어의 메소드 중 트랜잭션(commit, rollback)이 필요한 곳에 @Transactional 어노테이션을 부착합니다. 오류를 방지하기 위해 아래와 같이 작성하면 좋습니다.

 @Transactional("transactionManager") // bean으로 만든 트랜잭션 매니저의 아이디와 같도록

문의 | 코멘트 또는 yoonbumtae@gmail.com




0개의 댓글

답글 남기기

Avatar placeholder

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다