.
[프로젝트 ex02]
개발환경
- 자바 개발도구 : Java 8
- Open JDK 다운로드
- GA(General Availability)는 테스트가 완료된 정식 릴리즈 버전을 말함. 안정적이니 사용해도 된다.- 통합개발 환경(IDE) : STS, Eclipse
- STS5 : Eclipse + Spring 플러그인
- STS5.0.7버전 다운로드- WAS : Tomcat 8.5.94
- Tomcat 8.5.94 다운로드
- 데이터 베이스: MySQL 5.7
- MySQL Connector/J 8.0- Maven 빌드 도구 (Maven 1.8)
- Lombock : 의존성 자동화 도구 (다운로드)/Download 1.18.22
- HicariCP 2.7.8: DBCP 프레임워크 (다운로드)
- java 1.8
- springframwork 5.7.0 RELEASE
- spring-tx
spring-jdbc
spring-test - HIkariCP
MyBatis
myBatis-spring
Log4jdbc - Junit 4.12
lombok 1.18.0 - javax.servlet-api 3.0(3.1)
- maven-compiler-plugins 1.8
- 테이블생성
CREATE TABLE tbl_board (
bno INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(200) NOT NULL,
content VARCHAR(2000) NOT NULL,
writer VARCHAR(50) NOT NULL,
regdate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updatedate TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
- Dummy(더미) 데이터 추가
INSERT INTO tbl_board (title, content, writer)
VALUES ('테스트제목', '테스트내용', 'user00');
- root-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<!-- HikariCP 커넥션풀 관련 스프링 설정 파일 시작 -->
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<!-- <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/BOOK?useSSL=false"></property> -->
<!-- log4jdbc라이브러리를 추가하기 위해 설정 파일 시작 jdbcUrl,driverClassName수정 -->
<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
<property name="jdbcUrl" value="jdbc:log4jdbc:mysql://localhost:3306/BOOK?useSSL=false"></property>
<!-- log4jdbc라이브러리를 추가하기 위해 설정 파일 끝 -->
<property name="username" value="book_ex"></property>
<property name="password" value="1234"></property>
</bean>
<!-- Hikari CP configuration -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg ref="hikariConfig" />
</bean>
<!-- HikariCP 커넥션풀 관련 스프링 설정 파일 끝 -->
<!-- MyBatis와 연동 관련 스프링 설정 파일 시작 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- MyBatis-spring -->
<!-- sacn은 지정된 모든 MyBatis관련 어노테이션 처리 -->
<mybatis-spring:scan base-package="org.zerock.mapper"/>
<!-- MyBatis와 연동 관련 스프링 설정 파일 끝 -->
</beans>
영속/비지니스 계층의 CRUD구현
순서
- 테이블의 칼럼 구조를 반영하는 VO(Value Obkect) 클래스의 생성
- MyBatis의 Mapper 인터페이스의 작성/XML 처리
- 작성한 Mapper 인터페이스의 테스트
SQL Developer의 연결이 JDBC연결을 이용하여 별도의 JDBC 연결을 테스트 할 필요 없다
*영속성(persistency)이란 데이터를 영구적으로 저장하는 것을 의미합니다.
영속 계층의 구현 준비
1. VO 클래스의 작성
VO클래스를 생성하는 작업은 테이블 설계를 기준으로 작성한다.
2. Mapper 인터페이스와 Mapper XML
MyBatis는 SQL을 처리하는데 어노테이션이나 XML을 이용할 수 있다. 어노테이션은 간단한 SQL이 아닐경우 사용하면 유지보수성이 떨어질 수 있다.
1. root-context.xml에 mybatis-spring:scan 태그를 이용해 mapper 패키지 경로를 추가한다. 또는 mapperLocations을 추가
2. SQL문 처리 (어노테이션 또는 mapper XML을 생성)
Mapper 인터페이스
- BoardVO.java
package org.zerock.domain;
import java.util.Date;
import lombok.Data;
@Data
public class BoardVO {
private Long bno;
private String title;
private String content;
private String writer;
private Date regdate;
private Date updateDate;
}
- root-context.xml
<mybatis-spring:scan base-package="org.zerock.mapper"/>
- SQL Developer에서 먼저 확인하기( SQL이 문제없이 실행가능한지 확인하기 위해)
-BoardMapperTests.java
package org.zerock.mapper;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.zerock.domain.BoardVO;
import lombok.Setter;
import lombok.extern.log4j.Log4j;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class BoardMapperTests {
@Setter(onMethod_ = @Autowired)
private BoardMapper mapper;
@Test
public void testGetList() {
System.out.println("testGetList");
System.out.println(mapper.getList());
// mapper.getList().forEach(board -> log.info(board));
List<BoardVO> boards = mapper.getList(); //BoardVO클래스는 @Data 어노테이션에 의해 사용가능
for (BoardVO board : boards) {
log.info(board);
}
}
}
- mapper를 어노테이션을 이용
- BoardMapper.java
package org.zerock.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Select;
import org.zerock.domain.BoardVO;
public interface BoardMapper {
@Select("select * from tbl_board where bno > 0")
public List<BoardVO> getList();
}
- mapper를 XML파일을 이용
- BoardMapper.java
package org.zerock.mapper;
import java.util.List;
//import org.apache.ibatis.annotations.Select;
import org.zerock.domain.BoardVO;
public interface BoardMapper {
//@Select대신 BoardMapper.xml에 SQL문 처리
//@Select("select * from tbl_board where bno > 0")
public List<BoardVO> getList();
}
- src/main/resouces 내 폴더생성 : org/zerock/mapper/BoardMapper.xml
- 파을의 폴더 구조나 이름은 필수는 아니지만 root-context.xml에 scan태그를 사용하여 추가한 패키지 경로와 동일한 이름( org/zerock/mapper ) 으로 작성하는 편이 나중에 혼란스러운 상황을 피할 수 있다.(하나씩 폴더를 생성하기)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- XML 파일의 버전과 인코딩을 선언하는 부분 -->
<!-- 매퍼의 네임스페이스를 정의하는 부분 -->
<!-- namespace 속성값은 Mapper 인터페이스와 동일한 이름으로 작성 -->
<mapper namespace="org.zerock.mapper.BoardMapper">
<!-- 쿼리를 정의하는 부분 -->
<!-- id의 속성값은 메서드의 이름과 일치하게 작성
resultType 속성 값은 select 쿼리의 결과를 특정 클래스의 객체로 만들기 위해 설정 -->
<select id="getList" resultType="org.zerock.domain.BoardVO">
<!-- CDATA 섹션: 특수 문자를 포함하는 쿼리 텍스트 -->
<![CDATA[
select * from tbl_board where bno > 0
]]>
</select>
</mapper>
영속영역의 CRUD 구현
웹프로젝트 구조에서 마지막 영역이 영속 영역이지만, 실제로 구현을 가장 먼저 할 수 있는 영역도 영속 영역이다.
영속영역은 기본적으로 CRUD작업을 하기 때문에 테이블과 VO(DTO) 등 약간의 준비만으로도 비즈니스 로직과 무관하게 CRUD작업을 작성할 수 있다.
MyBatis는 내부적으로 JDBC의 PreparedStatement를 활용하고 필요한 파라미터를 처리하는 '?'에 대힌 치환은 '#{속성}'을 이용해 처리한다.
#{속성}: 플레이스 홀더문법(SQL인젝션을 방지)
* SQL인젝션(SQL Injection)은 악의적인 사용자가 입력된 데이터를 통해 SQL 쿼리를 조작하여 데이터베이스에 대한 공격을 시도하는 보안 취약점를 말한다.
1. create(insert) 처리
tbl_board테이블은 PK칼럼을 `AUTO_INCREMENT` 속성을(오라클은 시퀀스) 이용해서 자동으로 데이터가 추가될 때 번호가 만들어지는 방식을 사용한다.
이처럼 자동으로 PK값이 정해지는 경우에는 다음과 같은 2가지 방식으로 처리할 수 있다.
- insert만 처리되고 생성된 PK값을 알 필요 없는 경우
- insert문이 실행되고 생성된 PK값을 알아야 하는 경우 -> `selectKey`를 사용
- BoardMapper.java
public void insertSelectKey(BoardVO boardVO);
- BoardMapper.xml
- insert만 처리되고 생성된 PK값을 알 필요 없는 경우
<insert id="insertWithoutKey" useGeneratedKeys="true" keyProperty="bno" > insert into tbl_board (title, content, writer) values (#{title}, #{content}, #{writer}) </insert>
- insert문이 실행되고 생성된 PK값을 알아야 하는 경우( https://sujinpad.tistory.com/110)
- insert문 실행 전 PK값을 미리 받아오기=> 오라클 select seq_board.nextval form dual
<!-- @SelectKey와 사용. PK값을 미리 SQL을 통해 처리하고, 특정이름으로 결과를 보관함. @insert 할때 SQL문을 보면 #{bno}와 같이 이미 처리 된 결과를 이용하는것을 확인 할 수 있음. --> <insert id="insertSelectKey_Oracle_Query"> <selectKey keyProperty="bno" order="BEFORE" resultType="long"> select seq_board.nextval form dual </selectKey> insert into tbl_board (bno, title, content, writer) values (#{bno}, #{title}, #{content}, #{writer}) </insert>
- insert문 실행 후 PK값을 받아오기 => MySQl select last_insert_id()
<!-- order속성: <selectKey> 구문을 언제 사용할지 결정 --> <!-- last_insert_id(): MYSQL에서 사용되는 함수로 마지막으로 삽입된 AOUTO_INCREMENT값을 가져와 bno에 할당--> <insert id="insertSelectKey" parameterType="org.zerock.domain.BoardVO"> <selectKey resultType="java.lang.Long" keyProperty="bno" order="BEFORE" > select last_insert_id() </selectKey> insert into tbl_board (title, content, writer) values (#{title}, #{content}, #{writer}) </insert> <!-- MyBatis(JDBC)에서 getGeneratedKeys()가 PK값을 찾아 넣음/AUTO_INCREMENT를 사용할 수 없을때 --> <!-- useGeneratedKeys: JDBC의 getGeneratedKeys() 메서드를 사용하여 DB에서 자동으로 생성된 키값을 가져옴 --> <!-- keyProperty: 자동생성된 키값을 저장할 자바 객체의 속성을 정의하는 속성 --> <insert id="insertSelectKey_JDBCInsertPK" useGeneratedKeys="true" keyProperty="bno" > insert into tbl_board (title, content, writer) values (#{title}, #{content}, #{writer}) </insert>
- insert문 실행 전 PK값을 미리 받아오기=> 오라클 select seq_board.nextval form dual
- insert만 처리되고 생성된 PK값을 알 필요 없는 경우
- BoardMapperTest.java
2. read(select) 처리
insert가 된 데이터를 조회하는 방법은 PK를 이용한다.
BoardMapper의 파라미터에 BoardVO클래스의 bno타입 정보를 이용하여 처리
3. delete 처리
1) 데이터 삭제
DELETE FROM tbl_board
WHERE bno = 5;
2) delete() 작성
반환형을 int로 받기
4. update 처리