1. STS13_JDBC, spring-jdbc 버전 View, Update, Delete
** [src/main/java] com.lec.sts13_jdbc.board.beans > BWriteDAO.java
package com.lec.sts13_jdbc.board.beans;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementSetter;
import com.lec.sts13_jdbc.board.C;
public class BWriteDAO {
JdbcTemplate template;
public BWriteDAO() {
this.template = C.template;
}
// 전체 SELECT
public List<BWriteDTO> select() {
// 이렇게 간단해진다!
return template.query(C.SQL_WRITE_SELECT,
new BeanPropertyRowMapper<BWriteDTO>(BWriteDTO.class));
} // end select()
public int insert(final BWriteDTO dto) {
// 1. update() + PreparedStatementSetter()
// return
// template.update(C.SQL_WRITE_INSERT, new PreparedStatementSetter() {
// @Override
// public void setValues(PreparedStatement ps) throws SQLException {
// ps.setString(1, dto.getSubject());
// ps.setString(2, dto.getContent());
// ps.setString(3, dto.getName());
// }
// } );
// 2. update() + PreparedStatementCreator()
return
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
PreparedStatement ps = con.prepareStatement(C.SQL_WRITE_INSERT);
ps.setString(1, dto.getSubject());
ps.setString(2, dto.getContent());
ps.setString(3, dto.getName());
return ps;
}
});
} // end insert()
public BWriteDTO readByUid(final int uid){
BWriteDTO dto = null;
// 조회수 증가.
template.update(C.SQL_WRITE_INC_VIEWCNT, new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, uid);
}
});
// 읽기
List<BWriteDTO> list =
template.query(C.SQL_WRITE_SELECT_BY_UID, new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, uid);
}
}, new BeanPropertyRowMapper<BWriteDTO>(BWriteDTO.class));
if(list.size() > 0) dto = list.get(0); // 읽어온 글 (첫번째 아이템)
return dto;
} // end readByUid()
public BWriteDTO selectByUid(final int uid) {
BWriteDTO dto = null;
// 읽기
List<BWriteDTO> list =
template.query(C.SQL_WRITE_SELECT_BY_UID, new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, uid);
}
}, new BeanPropertyRowMapper<BWriteDTO>(BWriteDTO.class));
if(list.size() > 0) dto = list.get(0); // 읽어온 글 (첫번째 아이템)
return dto;
}
public int update(final BWriteDTO dto) {
int cnt = 0;
cnt = template.update(C.SQL_WRITE_UPDATE, new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, dto.getSubject());
ps.setString(2, dto.getContent());
ps.setInt(3, dto.getUid());
}
});
return cnt;
} // end update()
public int deleteByUid(final int uid) {
int cnt = 0;
cnt = template.update(C.SQL_WRITE_DELETE_BY_UID, new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, uid);
}
});
return cnt;
} // end deleteByUid()
} // end DAO
** [src/main/java] com.lec.sts13_jdbc.board.command > BViewCommand.java
package com.lec.sts13_jdbc.board.command;
import java.util.Arrays;
import java.util.Map;
import org.springframework.ui.Model;
import com.lec.sts13_jdbc.board.beans.BWriteDAO;
import com.lec.sts13_jdbc.board.beans.BWriteDTO;
public class BViewCommand implements BCommand {
@Override
public void execute(Model model) {
Map<String, Object> map = model.asMap();
int uid = (Integer)map.get("uid");
BWriteDAO dao = new BWriteDAO();
BWriteDTO dto = dao.readByUid(uid);
model.addAttribute("list", Arrays.asList(dto));
// Arrays.asList(new String[]{"aaa", "bbb"})
// Arrays.asList("aaa", "bbb")
} // end execute()
} // end Command
** [src/main/java] com.lec.sts13_jdbc.board.command > BSelectCommand.java
package com.lec.sts13_jdbc.board.command;
import java.util.Arrays;
import java.util.Map;
import org.springframework.ui.Model;
import com.lec.sts13_jdbc.board.beans.BWriteDAO;
import com.lec.sts13_jdbc.board.beans.BWriteDTO;
public class BSelectCommand implements BCommand {
@Override
public void execute(Model model) {
Map<String, Object> map = model.asMap();
int uid = (Integer)map.get("uid");
BWriteDAO dao = new BWriteDAO();
BWriteDTO dto = dao.selectByUid(uid);
model.addAttribute("list", Arrays.asList(dto));
} // end execute()
} // end Command
** [src/main/java] com.lec.sts13_jdbc.board.command > BUpdateCommand.java
package com.lec.sts13_jdbc.board.command;
import java.util.Map;
import org.springframework.ui.Model;
import com.lec.sts13_jdbc.board.beans.BWriteDAO;
import com.lec.sts13_jdbc.board.beans.BWriteDTO;
public class BUpdateCommand implements BCommand {
@Override
public void execute(Model model) {
Map<String, Object> map = model.asMap();
BWriteDTO dto = (BWriteDTO)map.get("dto");
BWriteDAO dao = new BWriteDAO();
int cnt = dao.update(dto);
model.addAttribute("result", cnt);
} // end execute()
} // end Command
** [src/main/java] com.lec.sts13_jdbc.board.command > BDeleteCommand.java
package com.lec.sts13_jdbc.board.command;
import java.util.Map;
import org.springframework.ui.Model;
import com.lec.sts13_jdbc.board.beans.BWriteDAO;
public class BDeleteCommand implements BCommand {
@Override
public void execute(Model model) {
Map<String, Object> map = model.asMap();
int uid = (Integer)map.get("uid");
BWriteDAO dao = new BWriteDAO();
int cnt = dao.deleteByUid(uid);
model.addAttribute("result", cnt);
} // end execute
} // end Command
** [src/main/java] com.lec.sts13_jdbc.board.controller > BoardController.java
package com.lec.sts13_jdbc.board.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.lec.sts13_jdbc.board.C;
import com.lec.sts13_jdbc.board.beans.BWriteDTO;
import com.lec.sts13_jdbc.board.command.BCommand;
import com.lec.sts13_jdbc.board.command.BDeleteCommand;
import com.lec.sts13_jdbc.board.command.BListCommand;
import com.lec.sts13_jdbc.board.command.BSelectCommand;
import com.lec.sts13_jdbc.board.command.BUpdateCommand;
import com.lec.sts13_jdbc.board.command.BViewCommand;
import com.lec.sts13_jdbc.board.command.BWriteCommand;
@Controller
@RequestMapping("/board")
public class BoardController {
private BCommand command;
private JdbcTemplate template;
public BoardController() {
super();
System.out.println("BoardController() 생성");
}
@Autowired
public void setTemplate(JdbcTemplate template) {
System.out.println("setTemplate() 호출");
this.template = template;
C.template = template;
}
@RequestMapping("/list.do")
public String list(Model model) {
command = new BListCommand();
command.execute(model);
return "board/list";
}
@RequestMapping("/write.do")
public String write(Model model) {
return "board/write";
}
@RequestMapping(value = "/writeOk.do", method = RequestMethod.POST)
public String writeOk(BWriteDTO dto, Model model) {
model.addAttribute("dto", dto);
new BWriteCommand().execute(model);
return "board/writeOk";
}
@RequestMapping("/view.do")
public String view(int uid, Model model) {
model.addAttribute("uid", uid);
new BViewCommand().execute(model);
return "board/view";
}
@RequestMapping("/update.do")
public String update(int uid, Model model) {
model.addAttribute("uid", uid);
new BSelectCommand().execute(model);
return "board.update";
}
@RequestMapping(value = "/updateOk.do", method = RequestMethod.POST)
public String updateOK(BWriteDTO dto, Model model) {
model.addAttribute("dto", dto);
new BUpdateCommand().execute(model);
return "board/updateOk";
}
@RequestMapping("/deleteOk.do")
public String deleteOk(int uid, Model model) {
model.addAttribute("uid", uid);
new BDeleteCommand().execute(model);
return "board/deleteOk";
}
}
2. Spring JDBC 일반적인 제작 순서
1) DAO 메소드()
: 트랜잭션 발생, JdbcTemplate 사용
2) BCommand의 execute()
3) Controller의 @RequestMapping
: handler 메소드()
4) View
3. JdbcTemplate 쿼리 메소드(추천, PSS : PreparedStatementSetter 객체)
SQL 명령 - 방식 - 추천 메소드 - 리턴값
SELECT - Statement - query("SQL", RowMapper<T>) - List<T>
SELECT - PreparedStatement - query("SQL", PSS, RowMapper<T>) - List<T>
DML 명령(INSERT, UPDATE, DELETE) - Statement - update("SQL") - int
DML 명령(INSERT, UPDATE, DELETE) - PreparedStatement - update("SQL", PSS) - int
DML 명령(INSERT, UPDATE, DELETE) - PreparedStatement - update(PreparedStatementCreator) - int
4. STS13_JDBC의 뷰는 SELECT와 조회수를 동시에 성공해야하는데 이 부분의 코드가 아직 미비하다,
이러한 부분을 보완하기 위해 아래의 STS14_Transaction 단원에서 트랜잭션을 배운다.
5. 트랜잭션
: 논리적 작업 단위,
트랜잭션을 구성하는 여러 작업 중 어떤 한 부분의 작업이 완료되었다 하더라도,
다른 부분의 작업이 완료되지 않을 경우 전체 트랜잭션은 취소되어야 한다.
즉, 트랜잭션을 완료하는 것을 커밋(commit), 트랜잭션을 취소하는 것을 롤백(rollback)
6. STS14_Transaction 시나리오
공연장 티켓 구매 Transaction
= (카드사, 결제 발생, 제약사항 없음) + (공연사, 티켓 발권 발생, 제약사항 : 한 번 구매에 최대 5매까지만 가능)
7. STS14_Transaction
** src > main > webapp > WEB-INF > web.xml에 utf-8 filter 장착
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 스프링 컨테이너 인코딩 설정 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
** Maven 설정 파일 pom.xml에 오라클 라이브러리와 spring jdbc 추가
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lec</groupId>
<artifactId>sts14_transaction</artifactId>
<name>STS14_Transaction</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>1.6</java-version>
<org.springframework-version>5.2.1.RELEASE</org.springframework-version>
<org.aspectj-version>1.6.10</org.aspectj-version>
<org.slf4j-version>1.6.6</org.slf4j-version>
</properties>
<!-- Maven 빌드를 사용하는 Spring 에서 오라클 라이브러리 추가하기 -->
<!--dependencies 위에 설정 -->
<repositories>
<repository>
<id>oracle</id>
<name>ORACLE JDBC Repository</name>
<url>https://code.lds.org/nexus/content/groups/main-repo</url>
</repository>
</repositories>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<!-- dependencies 안쪽 에 설정 -->
<!-- ojdbc6 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<!-- spring jdbc 사용 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.test.int1.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
[추가] 정상적으로 메이븐이 잘 다운되면 아래의 사진과 같이 spring-jdbc와 spring-tx가 확인됨
** ERD 파일 작성
[카드사] 어느 사용자 (user_id)가 얼마만큼 결제(buy_amount)한 정보
[공연장] 어느 사용자 (user_id)에게 몇 장이나 발권(ticket_count)한 정보, 제약사항 ticket_count는 1 이상 5 이하
1) test_transaction.erm
2) test_transaction.sql
/* Drop Tables */
DROP TABLE test_card CASCADE CONSTRAINTS;
DROP TABLE test_ticket CASCADE CONSTRAINTS;
/* Create Tables */
CREATE TABLE test_card
(
user_id varchar2(20) NOT NULL,
buy_amount number DEFAULT 1
);
CREATE TABLE test_ticket
(
user_id varchar2(20) NOT NULL,
ticket_count number NOT NULL,
CONSTRAINT ticket_by_limit
CHECK(ticket_count BETWEEN 1 AND 5)
);
SELECT * FROM test_card;
SELECT * FROM test_ticket;
-- ticket 2장을 카드로 결제하는 경우
INSERT INTO test_card VALUES('aaa', 2);
INSERT INTO test_ticket VALUES('aaa', 2);
-- ticket 5장을 카드로 결제하는 경우
INSERT INTO test_card VALUES('aaa', 5);
INSERT INTO test_ticket VALUES('aaa', 5);
-- ticket 6장을 카드로 결제하는 경우
INSERT INTO test_card VALUES('aaa', 6); -- 성공
INSERT INTO test_ticket VALUES('aaa', 6); -- 실패, ORA-02290: check constraint
** [src/main/java] com.lec.sts14_transaction > TicketDTO.java
package com.lec.sts14_transaction;
public class TicketDTO {
private String userId;
private int ticketCount;
public final String getUserId() {
return userId;
}
public final void setUserId(String userId) {
this.userId = userId;
}
public final int getTicketCount() {
return ticketCount;
}
public final void setTicketCount(int ticketCount) {
this.ticketCount = ticketCount;
}
}
** 트랜잭션 실패시 반드시 Rollback 수행해야한다.
이러한 트랜잭션 처리를 위해 스프링에서 제공하는 여러가지 방법
1) jdbc template 사용
: [src/main/java] com.lec.sts14_transaction > TicketDAO1.java
package com.lec.sts14_transaction;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
public class TicketDAO1 {
JdbcTemplate template;
public final void setTemplate(JdbcTemplate template) {
this.template = template;
}
public TicketDAO1() {
System.out.println(template);
}
// 티켓 구매
public void buyTicket(final TicketDTO dto) {
System.out.println("buyTicket()");
System.out.println("user id : " + dto.getUserId());
System.out.println("ticket count : " + dto.getTicketCount());
// 카드 결제
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
String query = "INSERT INTO test_card VALUES (?, ?)";
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, dto.getUserId());
pstmt.setInt(2, dto.getTicketCount());
return pstmt;
}
});
// 티켓 발권
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
String query = "INSERT INTO test_ticket VALUES (?, ?)";
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, dto.getUserId());
pstmt.setInt(2, dto.getTicketCount());
return pstmt;
}
});
}
}
2) PlatformTransactionManager 사용
: [src/main/java] com.lec.sts14_transaction > TicketDAO2.java
package com.lec.sts14_transaction;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
public class TicketDAO2 {
JdbcTemplate template;
public final void setTemplate(JdbcTemplate template) {
this.template = template;
}
// 트랜잭션 처리 객체 (인터페이스)
// PlatformTransactionManager (I)
// └─ AbstractPlatformTransactionManager (추상클래스)
// └─ ... 다양한 클래스 (DB 종류, 환경에 따른 객체들이 정의되어 있다)
PlatformTransactionManager transactionManager;
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public TicketDAO2() {
System.out.println(template);
}
// 티켓 구매
public void buyTicket(final TicketDTO dto) {
System.out.println("buyTicket()");
System.out.println("user id : " + dto.getUserId());
System.out.println("ticket count : " + dto.getTicketCount());
TransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(definition);
try {
// 카드 결제
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
String query = "INSERT INTO test_card VALUES (?, ?)";
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, dto.getUserId());
pstmt.setInt(2, dto.getTicketCount());
return pstmt;
}
});
// 티켓 발권
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
String query = "INSERT INTO test_ticket VALUES (?, ?)";
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, dto.getUserId());
pstmt.setInt(2, dto.getTicketCount());
return pstmt;
}
});
// 트랜잭션 성공 --> commit()
transactionManager.commit(status);
} catch(Exception e) {
// 트랜잭션 실패 --> rollback()
transactionManager.rollback(status);
throw e;
}
} // end buyTicket()
}
3) TransactionTemplate 사용
: [src/main/java] com.lec.sts14_transaction > TicketDAO3.java
package com.lec.sts14_transaction;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
public class TicketDAO3 {
JdbcTemplate template;
public final void setTemplate(JdbcTemplate template) {
this.template = template;
}
// TransactionTemplate 사용
TransactionTemplate transactionTemplate;
public final void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public TicketDAO3() {
System.out.println(template);
}
// 티켓 구매
public void buyTicket(final TicketDTO dto) {
System.out.println("buyTicket()");
System.out.println("user id : " + dto.getUserId());
System.out.println("ticket count : " + dto.getTicketCount());
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// 이 안의 작업들은 하나의 트랜잭션으로 동작한다.
// 카드 결제
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
String query = "INSERT INTO test_card VALUES (?, ?)";
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, dto.getUserId());
pstmt.setInt(2, dto.getTicketCount());
return pstmt;
}
});
// 티켓 발권
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
String query = "INSERT INTO test_ticket VALUES (?, ?)";
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, dto.getUserId());
pstmt.setInt(2, dto.getTicketCount());
return pstmt;
}
});
}
});
} // end buyTicket()
}
** src > main > webapp > WEB-INF > spring > appServlet > servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.lec.sts14_transaction" />
<!-- spring-jdbc 빈 객체 생성 -->
<!-- DataSource 객체 -->
<beans:bean name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<beans:property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
<beans:property name="username" value="scott0316"/>
<beans:property name="password" value="tiger0316"/>
</beans:bean>
<!-- JdbcTemplate -->
<beans:bean name="template" class="org.springframework.jdbc.core.JdbcTemplate">
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
<!-- DAO1 트랜잭션 미적용 버전 -->
<beans:bean name="dao1" class="com.lec.sts14_transaction.TicketDAO1">
<beans:property name="template" ref="template"/>
</beans:bean>
<!-- PlatformTransactionManger 빈객체 -->
<beans:bean name="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<beans:property name="dataSource" ref="dataSource" />
</beans:bean>
<!-- DAO2 트랜잭션 적용 -->
<beans:bean name="dao2" class="com.lec.sts14_transaction.TicketDAO2">
<beans:property name="template" ref="template"/>
<beans:property name="transactionManager" ref="transactionManager"/>
</beans:bean>
<!-- TransactionTemplate -->
<beans:bean name="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<beans:property name="transactionManager" ref="transactionManager"/>
</beans:bean>
<!-- DAO3 트랜잭션 적용 -->
<beans:bean name="dao3" class="com.lec.sts14_transaction.TicketDAO3">
<beans:property name="template" ref="template"/>
<beans:property name="transactionTemplate" ref="transactionTemplate"/>
</beans:bean>
</beans:beans>
** src > main > webapp > WEB-INF > views > buy_ticket.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>티켓 결제</title>
</head>
<body>
<p>카드 결제</p>
<form action="buy_ticket_card">
고객 아이디 : <input type="text" name="userId" > <br>
티켓 구매수 : <input type="text" name="ticketCount" > <br>
<input type="submit" value="구매" > <br>
</form>
</body>
</html>
** src > main > webapp > WEB-INF > views > buy_ticket_done.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>구매 완료</title>
</head>
<body>
<p>결제 완료</p>
고객 아이디 : ${ticketInfo.userId } <br />
티켓 구매수 : ${ticketInfo.ticketCount } <br />
<button onclick="history.back()">돌아가기</button>
</body>
</html>
** src > main > webapp > WEB-INF > views > buy_ticket_fail.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<script>
alert('트랜잭션 에러 발생. 이전페이지로 돌아갑니다');
history.back();
</script>
** [src/main/java] com.lec.sts14_transaction > HomeController 서블릿
package com.lec.sts14_transaction;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Handles requests for the application home page.
*/
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
return "home";
}
//private TicketDAO1 dao; // 트랜잭션 미적용
//private TicketDAO2 dao; // 트랜잭션 적용
private TicketDAO3 dao; // 트랜잭션 적용
@Autowired
//public void setDao(TicketDAO1 dao) {
//public void setDao(TicketDAO2 dao) {
public void setDao(TicketDAO3 dao) {
this.dao = dao;
}
@RequestMapping("/buy_ticket")
public String buy_ticket() {
return "buy_ticket"; // 티켓 구매 양식
}
// 티켓구매 처리(트랜잭션)
@RequestMapping("/buy_ticket_card")
public String buy_ticket_card(TicketDTO dto, Model model) {
System.out.println("/buy_ticket_card");
System.out.println("user id : " + dto.getUserId());
System.out.println("ticket count : " + dto.getTicketCount());
String page = "buy_ticket_done";
try {
dao.buyTicket(dto); // 트랜잭션이 발생되어야 함
model.addAttribute("ticketInfo", dto);
} catch(Exception e) {
e.printStackTrace();
page = "buy_ticket_fail"; // 트랜잭션 오류 발생시 페이지
}
return page;
}
}
8. 영속성 (Persistence)
: 데이터를 생성한 프로그램이 종료되더라도 사라지지 않는 데이터의 특성
9. Object Persistence(영구적인 객체)
: 메모리 상의 데이터를 파일 시스템, 관계형 데이터베이스 혹은 객체 데이터베이스 등을
활용하여 영구적으로 저장하여 영속성을 부여한다
Web Application ---Create, Update, Delete---> Database
Web Application <-------------Read------------- Database
10. 데이터를 데이터베이스에 저장하는 3가지 방법
1) JDBC : java에서 사용
2) Spring JDBC : JdbcTemplate
3) Persistence Framework : Hibernate, mybatis
11. ORM(Object Relational Mapping, 객체-관계 매핑)
: 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑(연결)해주는 것,
객체 지향 프로그래밍은 클래스를 사용하고, 관계형 데이터베이스는 테이블을 사용
객체 모델과 관계형 모델 간에 불일치가 존재하나 ORM을 통해 객체 간의 관계를 바탕으로
SQL을 자동으로 생성하여 불일치를 해결
데이터베이스 데이터 <-- 매핑 --> Object 필드 객체를 통해 간접적으로 데이터베이스 데이터를 다룬다.
Persistant API라고도 불린다(JPA, Hibernate, MyBatis 등)
12. MyBatis
: 자바 퍼시스턴스 프레임워크(persistent framework)의 하나로 XML과 애너테이션(annotation)을 사용하여
저장 프로시저나 SQL 문으로 객체들을 연결
** 과거 아파치재단에서 iBATIS로 개발했으나, 그 개발진들이 구글코드로 옮기면서 MyBatis로 이름을 바꾸어
계속 업데이트 중(iBATIS는 중단)
** 매우 간단하게 코드를 줄일 수 있고, 자바 코드가 아닌 XML매퍼파일을 통해서 DB에 접근도 가능하다
XML매퍼파일에 쿼리(SQL)만 설정해두어도 알아서 동작
13. STS15_MyBatis, base-package : com.lec.sts15_mybatis
** src > main > webapp > WEB-INF > web.xml에 utf-8 filter 장착
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 스프링 컨테이너 인코딩 설정 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
** 메이븐설정파일 pom.xml에서 Spring 5.2.1/Java 1.8/오라클 라이브러리/spring jdbc/myBatis/myBatis spring 추가
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lec</groupId>
<artifactId>sts15_mybatis</artifactId>
<name>STS15_MyBatis</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>1.6</java-version>
<org.springframework-version>5.2.1.RELEASE</org.springframework-version>
<org.aspectj-version>1.6.10</org.aspectj-version>
<org.slf4j-version>1.6.6</org.slf4j-version>
</properties>
<!-- Maven 빌드를 사용하는 Spring 에서 오라클 라이브러리 추가하기 -->
<!--dependencies 위에 설정 -->
<repositories>
<repository>
<id>oracle</id>
<name>ORACLE JDBC Repository</name>
<url>https://code.lds.org/nexus/content/groups/main-repo</url>
</repository>
</repositories>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
<scope>runtime</scope>
</dependency>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<!-- dependencies 안쪽 에 설정 -->
<!-- ojdbc6 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<!-- spring jdbc 사용 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- mybatis spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.test.int1.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
[추가] https://mvnrepository.com 이용, MyBatis 설정
[추가] https://mvnrepository.com 이용, MyBatis Spring 설정
[추가] MyBatis가 정상적으로 잘 다운되면 아래의 사진과 같이 mybatis, mybatis-spring 파일이 확인됨
** src > main > webapp > WEB-INF > spring > sppServlet > servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.lec.sts15_mybatis" />
<!-- spring-jdbc 빈 객체 생성 -->
<!-- DataSource 객체 -->
<beans:bean name="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<beans:property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<beans:property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
<beans:property name="username" value="scott0316"/>
<beans:property name="password" value="tiger0316"/>
</beans:bean>
<!-- JdbcTemplate -->
<beans:bean name="template" class="org.springframework.jdbc.core.JdbcTemplate">
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
</beans:beans>
[나머지는 spring-jdbc 버전 STS13_JDBC 복사 붙여 넣기,
오늘은 MyBatis 세팅 후 게시판 작동되겠끔만 수업 진행했고
내일은 MyBatis 를 이용한 게시판 만들 예정]
'웹_프론트_백엔드 > JAVA프레임윅기반_풀스택' 카테고리의 다른 글
2020.07.02 (0) | 2020.07.02 |
---|---|
2020.07.01 (0) | 2020.07.01 |
2020.06.29 (0) | 2020.06.29 |
2020.06.23 (0) | 2020.06.23 |
2020.06.22 (0) | 2020.06.22 |