티스토리 뷰

728x90
지금까지는 jar파일을 lib폴더에 직접 넣어서 DB와 연결했다면,
이번에는 Maven Respository 통해서 Spring framework에 적용할 수 있는

dependency를 추가해 DB와 연결해보자


Java ver

 

 

 

 

프로젝트 생성

Project Explorer에서 빈공간에 우클릭하면 Dynamic Web Project 만들 수 있다!
여기까진 할 수 있잖아

 

방금 만든 그 프로젝트를 Maven Project로 만들어주자!

혹시 다시 Maven 제거하고 싶다면, 위의 그림처럼 Disabled Maven Nature 해주믄 되니꽈

아! 메이븐 설정하고 프로젝트에 오류뜨면 그냥 alt+F5로 마무리해주십시다

 

 

 

 

 

 

Maven Repository에서 dependency 가져오기

MariaDB를 쓰고 있으니 Spring에서 mariadb를 사용할 수 있도록 해주는 dependency를 가져오자

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

 

 

자바를 통해 db에 연결할 수 있는 connector인 JDBC를 Spring에서 사용할 수 있도록 Spring JDBC를 가져온다

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

 

 

 

 DB와 연결할 수 있도록  Database Connection역할을 해주는 Spring DBCP를 가져오자

https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2/2.9.0

 

 

 

다음은 관계형 DB와 객체지향 애플리케이션을 쉽게 매핑해주는 MyBatis를 가져오자
이전에는 jar파일로 했었지

https://mvnrepository.com/artifact/org.mybatis/mybatis/3.5.7

 

 

MyBatis를 Spring에서 사용할 수 있도록 해주는 MyBatis-Spring도 가져오자

https://mvnrepository.com/artifact/org.mybatis/mybatis-spring

 

 

가져온 dependency를 토대로 해서 Maven 기본설정 해보자!

<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>Ex26MyBatisJava</groupId>
  <artifactId>Ex26MyBatisJava</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>

  <!-- 소스의 디렉토리 구조, 빌드 산출물 디렉토리 구조, 빌드시 사용할 플러긴 정보 관리 -->
  <build>
  	<!-- sourceDirectory : 자바 소스 코드를 관리하는 디렉토리 -->
  	<sourceDirectory>src/main/java</sourceDirectory>
  
	<!-- Maven제공 기능은 플러긴 기반으로 작동 따라서 설정 필요 -->  
    <plugins>
    
	  <!-- 컴파일 플러그인 설정 -->
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
        	<!-- 16>14변경 -->
          <release>14</release>
        </configuration>
      </plugin>
      
      <!-- 배포 패키징 플러긴 설정 -->
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.2.3</version>
        <!-- configuration 추가 -->
        <configuration>
        	<!-- 배포할 위치 directory -->
        	<warSourceDirectory>src</warSourceDirectory>
        </configuration>
      </plugin>
    </plugins>
  </build>
  
  <!-- 라이브러리 버전관리 : properties -->
  <properties>
  	<javax.servlet-version>4.0.1</javax.servlet-version>
  	<javax.servlet.jsp-version>2.3.3</javax.servlet.jsp-version>
  	<javax.servlet.jsp.jstl-version>1.2</javax.servlet.jsp.jstl-version>
  	<org.springframework>5.2.2.RELEASE</org.springframework>
  </properties>
  
  <!-- 라이브러리 셋팅 : dependencies 프로그램과 의존관계인 라이브러리 관리 -->
  <dependencies>
  
  	<!-- servlet-api 라이브러리 적용 위한 설정 -->
	<dependency>
	    <groupId>javax.servlet</groupId>
	    <artifactId>javax.servlet-api</artifactId>
	    <!-- 버전관리는 properties에서 하기 때문에 변수처럼 사용하기 -->
	    <version>${javax.servlet-version}</version>
	    <scope>provided</scope>
	</dependency>
	
  	<!-- javax.servlet.jsp-api 라이브러리 적용 위한 설정 -->
	<dependency>
	    <groupId>javax.servlet.jsp</groupId>
	    <artifactId>javax.servlet.jsp-api</artifactId>
	    <!-- 버전관리는 properties에서 하기 때문에 변수처럼 사용하기 -->
	    <version>${javax.servlet.jsp-version}</version>
	    <scope>provided</scope>
	</dependency>

	<!-- javax.servlet/jstl 라이브러리 적용 위한 설정 -->
	<dependency>
	    <groupId>javax.servlet</groupId>
	    <artifactId>jstl</artifactId>
	    <!-- 버전관리는 properties에서 하기 때문에 변수처럼 사용하기 -->
	    <version>${javax.servlet.jsp.jstl-version}</version>
	</dependency>
	
	<!-- spring-webmvc 라이브러리 적용 위한 설정 -->
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-webmvc</artifactId>
	    <!-- 버전관리는 properties에서 하기 때문에 변수처럼 사용하기 -->
	    <version>${org.springframework}</version>
	</dependency>
	
	<!-- resource 어노테이션 안먹혀서 추가함.. 요상하군... -->
	<dependency>
		<groupId>javax.annotation</groupId>
		<artifactId>javax.annotation-api</artifactId>
		<version>1.3.2</version>
	</dependency>
	
	<!-- validation-api를 사용하기 위한 설정 -->
	<dependency>
	    <groupId>javax.validation</groupId>
	    <artifactId>validation-api</artifactId>
	    <version>2.0.1.Final</version>
	</dependency>
	
	<!-- hibernate-validator를 사용하기 위한 설정 -->
	<dependency>
  	  	<groupId>org.hibernate.validator</groupId>
  	 	<artifactId>hibernate-validator</artifactId>
 	 	<version>6.2.1.Final</version>
	</dependency>
	
	
	<!-- jar파일 사용하지 않고 라이브러리로 사용하기 -->
	<!-- mariadb를 연결하기 위한 라이브러리로 jar파일 대신 -->
	<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
	<dependency>
	    <groupId>org.mariadb.jdbc</groupId>
	    <artifactId>mariadb-java-client</artifactId>
	    <version>2.7.4</version>
	</dependency>
	<!-- Spring JDBC -->
	<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-jdbc</artifactId>
	    <version>5.2.9.RELEASE</version>
	</dependency>
	<!-- Connection역할 -->
	<!-- 트랙잭션 제어 역할 -->
	<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
	<dependency>
	    <groupId>org.apache.commons</groupId>
	    <artifactId>commons-dbcp2</artifactId>
	    <version>2.9.0</version>
	</dependency>
	
	<!-- MyBatis를 사용하기 위한 라이브러리 -->
	<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis/3.5.7 -->
	<dependency>
	    <groupId>org.mybatis</groupId>
	    <artifactId>mybatis</artifactId>
	    <version>3.5.7</version>
	</dependency>
	
	<!-- MyBatis-spring을 사용하기 위한 라이브러리 -->
	<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring/2.0.6 -->
	<dependency>
	    <groupId>org.mybatis</groupId>
	    <artifactId>mybatis-spring</artifactId>
	    <version>2.0.6</version>
	</dependency>
	
	
	
	
	
  </dependencies>
</project>

@Resource가 안먹혀서 dependency 추가함..
여기선 Validator안쓸 것 같긴 한데, 만약 쓴다면 또 가져와야 하므로 그냥 넣어뒀다~

 

 

 

 

 

 

 

Spring MVC Java 기본설정

일단 Spring MVC를 어떻게 구현할지 구조부터 대충 살펴보면,
전에 JSP MVC 패턴때 BeansConfigClass 만들어서 bean을 정의해주었지
그 역할을 이젠 SpringConfigClass라는 이름으로 변경해 Java버전으로 해볼게!

import javax.servlet.Filter;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class SpringConfigClass extends AbstractAnnotationConfigDispatcherServletInitializer {

	//AbstractAnnotationConfigDispatcherServletInitializer의 메소드 전부 override
	
	//프로젝트에서 사용할 bean정의 클래스 지정
	@Override
	protected Class<?>[] getRootConfigClasses() {
		// TODO Auto-generated method stub
		//return 추가
		return new Class[] {RootAppContext.class};
	}

	//Spring MVC 프로젝트 설정 위한 configuration 클래스 지정
	@Override
	protected Class<?>[] getServletConfigClasses() {
		// TODO Auto-generated method stub
		return new Class[] {ServletAppContext.class};
	}

	//DispatcherServlet에 매핑할 요청 주소 세팅
	//DispatcherServlet : 클라이언트 요청을 맨 처음 받는 위치로 공통작업 후 세부 작업은 담당 컨트롤러로 전달하는 역할
	@Override
	protected String[] getServletMappings() {
		// TODO Auto-generated method stub
		return new String[] {"/"};
	}
	
	//얘는 안따라와서 따로 추가해줌
	//request.setCharacterEncoding("UTF-8") 한 번만 설정하기
	@Override
	protected Filter[] getServletFilters() {
		// TODO Auto-generated method stub
		CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
		encodingFilter.setEncoding("UTF-8");
		return new Filter[] {encodingFilter};
	}
	
}

AbstractAnnotationConfigDispatcherServletInitializer를 상속받아서 그 내부 메서드를 override 받았어.
맨 마지막 filter 메서드는 안따라오더라고?
걍 내가 불렀어 filter적고 ctrl+space하믄 뜰겨

보면 먼저 bean 뭐 쓸지 getRoot~()메서드에서 정의해주고,
getServletConfigClasses()메서드에서 Configuration을 지정해준다
getServletMapping()메서드는 말그대로 매핑 역할을 하는데, 위에서 적었다시피 클라이언트 요청 받으면
그 요청에 해당하는 컨트롤러 쪽으로 보내주는 것이 바로 매핑!
맨 마지막 filter 메서드는 우리 맨~날 스크립틀릿으로 또는 컨트롤러에서 request.set~("UTF-8") 적어줬잖아?
그걸 저기다가 딱 한 번, 두 번도 아니고 한 번만 적어주면 페이지마다 적은 효과를 주는거지

 

 

이제 getRootConfigClass 메서드에서 정의될 bean 클래스 RootAppContext를 한 번 만들어보자

import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import kr.co.goodee39.mapper.MapperInterface;

//Spring MVC 프로젝트에서 사용할 bean 정의하는 클래스
@Configuration
//db.properties에서 정의한 내용 가져오기
@PropertySource("/WEB-INF/properties/db.properties")
public class RootAppContext {
	
	//db.properties에서 정의한 이름, 값 삽입해서 mariaDB와 연결
	//Value어노테이션으로 해당하는 값 가져올 수 있음
	@Value("${db.classname}")
	private String classname;
	
	@Value("${db.url}")
	private String url;
	
	@Value("${db.username}")
	private String username;
	
	@Value("${db.password}")
	private String password;
	
	
	//DB 접속 정보 관리
	@Bean
	public BasicDataSource dataSouce() {
		BasicDataSource source = new BasicDataSource();
		source.setDriverClassName(classname);
		source.setUrl(url);
		source.setUsername(username);
		source.setPassword(password);
		
		return source;
	}
	
	//쿼리문과 DB접속 관리를 위한 객체
	@Bean
	public SqlSessionFactory factory(BasicDataSource source) throws Exception {
		
		SqlSessionFactory factory = null;
		
		try {
			SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
			factoryBean.setDataSource(source);
			factory = factoryBean.getObject();
			
		} catch (Exception e) {
			e.printStackTrace();
		} 
		
		return factory;
	}
	
	//쿼리문 실행을 위한 객체
	//Mapper를 Bean등록하기
	@Bean
	public MapperFactoryBean<MapperInterface> testMapper(SqlSessionFactory factory) throws Exception {
		
		MapperFactoryBean<MapperInterface> factoryBean = new MapperFactoryBean<MapperInterface>(MapperInterface.class);
		factoryBean.setSqlSessionFactory(factory);
		
		return factoryBean;
			
	}
    
    	
	//트랜잭션 설정 객체1
	@Bean
	public DataSourceTransactionManager transactionManager() {
		return new DataSourceTransactionManager(dataSource());
	}
	
	//트랜잭션 설정 객체2
	@Bean
	public TransactionTemplate transactionTemplate() {
		return new TransactionTemplate(transactionManager());
	}
	
}

@Configuration이 전부였던 RootAppContext에 @PropertyScource("path")를 준 이유는!
직접적으로 여기에 db연결을 위한 클래스 이름, url, username, password를 적지 않기 위함이다
후에 저 필드명에 맞춰서 properties파일을 이용해 값을 삽입해 줄 것임!

DB접속 정보 관리를 위해
DBCP2의 디팬던시를 추가했기 때문에 사용 가능한 이 BasicDataSource객체를 이용해서 JDBC 연결을 설정해주고,

쿼리문과 DB 접속 관리를 위해
Mybatis 통한 SqlSessionFactory 객체를 이용해 try ~catch문 안에서
BasicDataSource로 설정한 JDBC연결 설정을 주입해준다!
그럼 연결 쏵~
 

마지막으로 MapperInterface를 제너럴로 갖는 MapperFactoryBean객체 타입을 반환하는 testMapper 메서드에
SqlSessionFactory 객체를 쇽 넣어주어 쿼리문이 실행 및 결과값을 반환하도록 설정
밑에서 MapperInterface 만들거임!

 

 

아니 마지막은 이거거든?

트랜잭션 관리를 위한 bean객체를 정의해주는데,
DataSourceTransactionManager와 TransactionTemplate 객체를 받아준다!
이 두개를 가지고 트랜잭션을 제어할 수 있는데
트랜잭션이라 함은, 은행 업무를 예로 들 수 있지

내가 10만원 뽑으러 ATM기를 찾았어
내 계좌든, 카드든, 어플이든 이용해서 나를 인증하고
그 인증이 통과해야 ATM기에서 돈을 주잖아?

만약 그 인증 과정 중에서 하나라도 틀리면 어떻게 돼?
처음 화면으로 돌아가버리지.. 개빡치게
물론 보안을 위해선 그게 좋겠지... ^^

아무튼 어떤 작업이 있을 때 그 작업이 완전하게 이루어지기 위해
필요한 일련의 업무들이 있잖아?
그 때 그 업무 중에 하나라도 실패하고, 망가지면 모든 것을 수포로 되돌리는 그런 것을 트랜잭션 제어한다고 해
일부만 성공했다고 ATM기에서 돈 줘버리면 뭐야... 내 돈 돌려줘요...

 

 

 

 

 

properties파일에 db 연결 정보 정의
 db.classname = org.mariadb.jdbc.Driver
 db.url = jdbc:mariadb://127.0.0.1:3306/scott
 db.username = root
 db.password = 비밀이지롱

요로케 설정하면 위에서 알아서 해당하는 이름 찾아서 쏙쏙 가져간다~ 이 말썸!

 

 

 

 

 

이번엔 getServletConfigClass 메서드에서 Spring MVC 설정을 해줄 Configuration 클래스를 정의해보자

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

//Spring MVC 프로젝트 설정 클래스
@Configuration

//@Controller 셋팅된 크래스를 Controller로 등록
@EnableWebMvc

//스캔할 패키지 저장
@ComponentScan("kr.co.goodee39.controller")
public class ServletAppContext implements WebMvcConfigurer {
	
	//Controller의 메서드가 반환하는 jsp의 이름 앞뒤에 경로(/WEB-INF/views), 확장자(.jsp) 추가하는 설정
	//configureViewResolvers
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		// TODO Auto-generated method stub
		WebMvcConfigurer.super.configureViewResolvers(registry);
		registry.jsp("/WEB-INF/views/", ".jsp");
	}
	
	//정적 파일(img, video, audio, etc) 경로 매핑
	//addResourceHandlers
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		// TODO Auto-generated method stub
		WebMvcConfigurer.super.addResourceHandlers(registry);
		registry.addResourceHandler("/**").addResourceLocations("/resources/");
	}
}

좀 낯설긴한데, servlet이라는 단어는 주구장창 봐왔잖아?
한 페이지 이동 할 때마다 servlet 추가추가추가 하던 그런 날이 끝났다 이거임..
여기서 한 큐에 설정이 가능해

일단 이 클래스가 Configuration할 클래스임을 어노테이션으로 명명하고,
@EnableWebMvc를 통해 이따가 @Controller 역할을 할 클래스를 진짜 controller로서 역할 할 수 있게 만들어줘
@ComponentScan으로 경로 설정해주면 그 내부 클래스 중 @Component설정된 클래스를 찾게될건데,
사실 이번에는 @Component로 등록된 클래스는 없어ㅎㅎㅎ


그리고나서 WebMvcConfigurer 상속받아준 후에 RootConfigClass와 마찬가지로 내부 메서드를 모두 상속 받아줘

configViewResolvers(ViewResolverResistry registry)라는 메서드는 이따가 @Controller에서 어떤 페이지로 갈 수 있게끔
jsp의 이름을 반환할텐데, 그 앞전 경로를 설정해주고, jsp파일임을 명시하는 확장자 설정이 가능해

addResourceHandelers(ResourceHandlerRegistry registry) 메서드는 정적인 파일, 예를들면 이미지, 영상, 음성 등등
어딘가에 딱 넣어두고, 불러와지기만 하는 변함없는 파일이잖아?
그 경로를 설정할 수 있는데, 여기서 보면 느끼겠지만 경로가 하나뿐이잖아..
하나의 경로밖에 못써..
뭐 하나의 경로 안에 폴더를 또 만들면 쓸 수 있겠지?
아 여기서 사실 addResourceHandler("/**")는 아직 어떤 역할인건지 잘 모르겠어.. 찾아보자 더 ㅎㅎㅎㅎ

 

자 여기까지가 기초다 기초...
사실 아직 100% 이해된건 아니야.
이렇다니까 일단 받아들인거고, 계속 따라쳐보고, 구글링 해보면서 내것으로 만들고 또 발전도 시켜봐야지!

 

 

Controller 클래스 만들기

아까 위에서 Spring MVC를 설정하게 해주는 클래스 하나가 있었잖아?
그 클래스 내부에 @EnableWebMvc라는 어노테이션이 있었는데
이건 @Controller로 지정된 클래스가 정말 controller로서의 역할을 할 수 있게끔 해주는 어노테이션이였어.
그럼 이제 @Controller를 달고있을 클래스를 만들어보자
따지고보면 JSP에서 Servlet역할의 일부를 담당하는 것이라고도 생각할 수 있지 않나? 싶네

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {
	
	//ServletAppContext에서 설정한 configureViewResolvers()메소드 통해서 
	///WEB-INF/views/index.jsp로 변환됨
	@RequestMapping(value="/", method=RequestMethod.GET)
	public String home() {
		return "index";
		
	}
}

별건 없어
그냥 무슨 역할하는지 보자고!

위에서 계속 언급했듯 이 클래스는 controller의 역할을 할 클래스이므로 어노테이션으로 이름표 달아주고,
메소드를 하나 만드는데, 이 메소드는 무어냐
첫 페이지 역할, 그러니까 home 역할을 할 페이지를 설정하는 메서드야
그래서 이름도 home이야 후후
value는 우리 JSP MVC 패턴에서 servlet만들면 맨 상단에 

@WebServlet("/블라블라")하는 공간이 있어 (ㄴㅇㄹ는 무시혀~ 걍 쓴거임)
저 블라블라는 보통 servlet이름이 들어가는데, 파일을 실행시켜서 이동한다거나 하면
url에 저기 작성된 이름이 뒤에 뜨더라고?
근데 이걸 /만 남기고 지워주면?!
그냥 RequestDispatcher rdp = request.getRequestDispatcher("");에서 설정한 jsp파일이 home으로 떠부러
servlet이라는 위치가 jsp파일 앞전에 안붙기 때문이지!
그거랑 같은 이치야 value = "/"는

아무턴, return "문자열";은 이따가 만들 home이 될 jsp파일 이름을 넣어준거야
이 controller가 실행되면 index라는 홈이름이 전달되겠지 어디로?
아까 위에서 @EnableWebMvc로 지금 이 클래스에 Controller의 자격을 준 servletAppContext 클래스로!

그리고는 configureViewResolvers(ViewResolverRegistry registry) 메소드를 만나 
/WEB-INF/views/index.jsp로 변신!
그리고 index.jsp에 이미지가 있다면 addResourceHandlers(ResourceHandlerRegistry registry) 메서드 통해서
src 앞에 /resources/가 콱 붙어주겠지~ 

 

 

 

 

 

 

DB에 테이블 하나 만들어 놓기

 

컬럼 3개를 갖는 springsample 테이블 하나를 생성해두었음!

 

 

 

 

 

 

HomeController에서 설정한 첫 페이지가 되어줄 index.jsp를 만들자
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<!-- SPRING + MYBATIS -->
	<h1>SPRING + MYBATIS</h1>
	<br />
	
	<a href="input_data">Input_Data</a><br />
	<a href="read_data">Read_Data</a><br />
	<br />
	<hr />
	<h3>트랜잭션</h3>
	<a href="tx_data1">트랜잭션_data1</a><br />
	<a href="tx_data2">트랜잭션_data2</a><br />
</body>
</html>

input_data 페이지로 이동시 DataVO1를 커맨드 객체로 받는 inputData메서드가 실행된다!
read_data 페이지로 이동시 DataVO1를 커맨드 객체로 받는 readData메서드가 실행된다!

트랜잭션은 tx_data1, tx_data2는 오류가 발생하도록 해놓은 상태다.
쿼리 결과가 어떻게 진행되는지 보자 



 

 

 

 

 

VO객체 만들기
public class DataVO1 {

	private String column1;
	private String column2;
	private String column3;
	
	public DataVO1() {
		// TODO Auto-generated constructor stub
	}

	public String getColumn1() {
		return column1;
	}

	public void setColumn1(String column1) {
		this.column1 = column1;
	}

	public String getColumn2() {
		return column2;
	}

	public void setColumn2(String column2) {
		this.column2 = column2;
	}

	public String getColumn3() {
		return column3;
	}

	public void setColumn3(String column3) {
		this.column3 = column3;
	}
	
	
}

요르케 DB에 맞춰서 컬럼따라 설정하고, getter, setter 메서드를 만들어서 
다른데서 가져다가 설정하고, 사용도 할 수 있도록 한다

 

 

 

 

 

 

 

 

MapperInterface만들기
import java.util.List;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;

import kr.co.goodee39.vo.DataVO1;
//Mapper내에서 Bean등록이 아니라 RootAppContext에서 등록함
public interface MapperInterface {


	//input_data에서 form내 input으로 추가될 row 설정
	@Insert("INSERT INTO springsample(column1, column2, column3) VALUES(#{column1}, #{column2}, #{column3})")
	public void insertData(DataVO1 vo);
	
	//추가된 내용 불러올 select문
	@Select("SELECT column1, column2, column3 FROM springsample ")
	public List<DataVO1> selectDataList();
	
}

사실상 쿼리문이라고 볼 수 있지.
그리고 DataVO1객체를 커맨드로 받아 내부 필드 값을 주입할 수 있도록 설정!

MapperInterface를 제너럴로 갖는 MapperFactoryBean객체 타입을 반환하는 testMapper 메서드 통해서
Bean을 등록했기 때문에 따로 등록 필요 없음!

@Select는 주입된 DB 뽑아보려고~

 

 

 

 

 

 

 

Controller 만들기
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import kr.co.goodee39.mapper.MapperInterface;
import kr.co.goodee39.vo.DataVO1;

@Controller
public class TestController {
	
	//MapperInterface객체를 Autowired로 주입
	@Autowired	
	MapperInterface mapper1;
	
	//RootAppContext에서 정의한 DataSourceTransactionManager을 Autowired로 주입
	//트랜잭션 제어 구VER
	@Autowired	
	DataSourceTransactionManager transactionManager;
	
	//RootAppContext에서 정의한 TransactionTemplate을 Autowired로 주입
	//트랜잭션 제어 신VER
	@Autowired
	TransactionTemplate transactionTemplate;
	
	

	@GetMapping("/input_data")
	public String inputData(DataVO1 vo1) {
		//DataVO1 커맨드 객체 받아서 form:form의 modelAttribute와 연결
		return "input_data";
	}
	
	//
	@PostMapping("/input_pro")
	public String inputPro(DataVO1 vo1) {
		//필요한 쿼리를 mapper통해 실행
		//DataVO1 커맨드 객체 내 필드를 집어넣어서 실행!
		mapper1.insertData(vo1);
		
		return "input_pro";
	}
	
	@GetMapping("/read_data")
	public String readData(Model model) {
		
		/*
		 * 두 줄은 너무 길어~
		List<DataVO1> list = mapper1.selectDataList();
		model.addAttribute("list", list);
		*/
		
		model.addAttribute("list", mapper1.selectDataList());
		
		return "read_data";
	}
	
	/*
	@GetMapping("/tx_data1")
	public String txData1(Model model) {
		
		try {
			
			DataVO1 vo1 = new DataVO1();
			DataVO1 vo2 = new DataVO1();
			
			//vo1은 길이 10에 맞는 값을 넣어서 잘 실행되고, 심지어 DB에도 반영됨
			vo1.setColumn1("오늘은");
			vo1.setColumn2("12월");
			vo1.setColumn3("28일");
			//vo2는 col2에서 길이 10 이상의 값이 있기 때문에 오류 발생
			vo2.setColumn1("내일은");
			vo2.setColumn2("오류를 위한 길이 10 넘치는 값 설정하기");
			vo2.setColumn3("29일");
			// 둘 다 정상적으로 실행되어야 vo1, vo2 반영되게 만들려는 것이 본 목적임
			
			mapper1.insertData(vo1);
			mapper1.insertData(vo2);
			
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "tx_data1";
	}
	*/
	
	//구ver 트랜잭션 제어
	//둘 다 정상적으로 실행되어야 vo1, vo2 반영되게 만들려는 것이 본 목적임
	//하나라도 오류시 전부 rollback
	@GetMapping("/tx_data1")
	public String txData1() {
		//Autowired로 받아온 DataSourceTransactionManager통해 Definition과 Status 내장 객체를 가져온다
		//전역으로 선언해서 사용할 수도 있지만 구VER이라 그냥 얘만 적용해쒀
		TransactionDefinition def =  new TransactionTemplate();
		TransactionStatus status = this.transactionManager.getTransaction(def);
		
		try {
			
			DataVO1 vo1 = new DataVO1();
			DataVO1 vo2 = new DataVO1();
			
			vo1.setColumn1("오늘은");
			vo1.setColumn2("12월");
			vo1.setColumn3("28일");
			//vo2는 col2에서 길이 10 이상의 값이 있기 때문에 오류 발생
			vo2.setColumn1("내일은");
			vo2.setColumn2("오류를 위한 길이 10 넘치는 값 설정하기");
			vo2.setColumn3("29일");
			
			//MapperInterface내의 insert어노테이션에 삽입된 쿼리 실행(vo1, vo2를 담아서!)
			mapper1.insertData(vo1);
			mapper1.insertData(vo2);
			
			//둘 다 정상 실행시 commit
			this.transactionManager.commit(status);
			
		} catch (Exception e) {
			e.printStackTrace();
			//status에 에러발생 시 rollback : vo1도 반영되지 않음
			this.transactionManager.rollback(status);
			
			return "tx_error";

		}
		return "tx_data1";
	}
	
	//신 ver 트랜잭션 제어
	@GetMapping("/tx_data2")
	public String txData2() {
		try {
			this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
				
				@Override
				protected void doInTransactionWithoutResult(TransactionStatus status) {
					DataVO1 vo1 = new DataVO1();
					DataVO1 vo2 = new DataVO1();
					
					vo1.setColumn1("오늘은");
					vo1.setColumn2("12월");
					vo1.setColumn3("28일");
					//vo2는 col2에서 길이 10 이상의 값이 있기 때문에 오류 발생
					vo2.setColumn1("내일은");
//					vo2.setColumn2("오류를 위한 길이 10 넘치는 값 설정하기");
					vo2.setColumn2("12월");
					vo2.setColumn3("29일");
					
					//MapperInterface내의 insert어노테이션에 삽입된 쿼리 실행(vo1, vo2를 담아서!)
					mapper1.insertData(vo1);
					mapper1.insertData(vo2);
					
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
			return "tx_error";
		}
		return "tx_data2";
	}
}

먼저 Bean등록한 MapperInferface / DataSourceTransactionManager / TransactionTemplate를
@Autowired로 주입해서 모두가 쓸 수 있도록 헌다

1) inputData()  메서드

input_data url타고 실행된 메서드 inputData에서 DataVO1를 주입받아줍니다으~
그래야 form:form에 modelAttribute로 설정해둔 dataVO1을 받아줄 수 있찌유~

2) inputPro() 메서드

이동한 input_data페이지에서는
form:form태그에서 input_pro로 설정된 action을 받아 inputPro 메서드가 실행된다
똑같이 DataVO1을 커맨드로 받아주고,
input_data페이지에서 넘겨받아 설정된 vo1의 값들을
상단에서 주입한 MapperInterface객체 타입의 mapper1통해 내부 메서드를 실행시킨다!!

성공적으로 수행되면 input_pro페이지로 이동되는데,
성공적 수행이라 함은 DB에 값이 잘 저장되었다 이말썸~

3)  readData()  메서드

DB를 한 번 읽어서 출력해보려는 심산이얌
inputPro 메서드 통해서 잘 주입되었다면 좌르륵 나오겠지?

 

 

 

그 밑에 주석처리 포함 장황한 메서드 3개는 트랜잭션을 제어하기 위한 수단으로
@Autowired로 주입한 DataSourceTransactionManager / TransactionTemplate를 사용해
두 가지 방법으로 시도해 볼 것임

4)  txData1() 메서드

DataSourceTransactionManager객체를 이용하는 방법으로,
구ver이라고 볼 수 있음

TransactionDefinition / TransactionStatus 요 두개를 받아와서
Transaction의 정의, 처리상태를 확인해 commit할 것인가 rollback할 것인가를 정해
try문 내부를 보면 예시로 DataVO1 객체 타입 받아와서 값 설정해가지고 한 번 넣어본 단이 있을거야

현재 테이블 컬럼 설정이 길이 10으로 제한되어 있는데
다른 건 다들 맞는데
vo2.setColumn2를 보면 10자는 넉근히 넘어가지
그럼 오류란 말이야
만약 트랜잭션 처리가 없었다면
vo1은 맞으니까 그냥 DB로 바로 commit 시켜버려
둘 다 맞아떨어져야 비로소 commit되는 것이 인지상정 아니겄음?
주석 처리된 txData1()메서드는 그렇게 돼

 catch문에서 rollback을 설정해주믄 됩니다으~
그리고 return을 error페이지 하나 파서 거그로 보내주는겨

vo1, vo2 둘 다 잘 들어갔다면
return tx_data1으로!

얘는 오류 발생하도록 설정해뒀어

 

5) txData2()  메서드

TransactionTemplate 요 객체를 이용한 트랜잭션 제어로, 

this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {...} }

위의 구문을 이용하쥐
결과 값 도출 없이 트랜잭션 상에서 에러나 예외 발생하면 rollback시켜버린다
보기에는 길지만 뭐 override로 금방 주입할 수 있어

 

DataVO통해서 설정한 값을 mapper1에 대입해서 insert쿼리에 대입해 실행시켜~
만약 에러 발생하면 tx_error페이지로 이동하고, 정상적으로 수행되면 tx_data2로 이동할꾸

 일단 값은 정상수행으로 넣어봤어

 

자바는 controller상에서 @로 주입해서 사용하니까 바로바로 try  ~ catch 적용해서
예외처리페이지로의 이동까지 설정이 가능한데,
XML은 아닌 듯 허다.... 아쉽다
아님 내가 모르는 방법이 있겠쥐~

 

 

 

 

 

 

 

이동될 페이지 만들기!
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<!-- SPRING + MYBATIS -->
	<h1>SPRING + MYBATIS</h1>
	<br />
	
	<form:form modelAttribute="dataVO1" action="input_pro" method="post">
		data1 : <form:input path="column1" type="text"/><br />
		data2 : <form:input path="column2" type="text"/><br />
		data3 : <form:input path="column3" type="text"/><br />
		<form:button type="submit">확인</form:button><br />
	</form:form>
	
</body>
</html>

얘가 바로 input_data페이지로 modelAtrribute를 dataVO1으로 가지고 있으며
action이 input_pro로 inputPro메서드를 실행시키게 된다
data1, 2, 3는 각각 column1, 2, 3데이터에 주입될 값이다

 

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<!-- SPRING + MYBATIS -->
	<h1>SPRING + MYBATIS</h1>
	<br />
	<h1>Input_Pro DB에 데이터 저장 완</h1>
</body>
</html>

 

성공적으로 mapper.insertData(vo1)수행되면 넘어갈 페이지고
DB에 반영이 되었다는 증거~

 

 

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<!-- SPRING + MYBATIS -->
	<h1>SPRING + MYBATIS</h1>
	<br />
	<!-- readData메서드에서 처리된 model을 통해 설정한 list라는 attribute를={list} obj이란 이름으로 받아준다 -->
	<c:forEach var="obj" items="${list}">
		<!-- {명명한 이름.컬럼명}으로 출력-->
		<li>${obj.column1}, ${obj.column2}, ${obj.column3}</li>
	</c:forEach>
	

</body>
</html>

read_data 성공적으로 출력시 요기 도착해서 데이터 보여줌!

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<!-- SPRING + MYBATIS -->
	<h1>SPRING + MYBATIS</h1>
	<br />
	<h3>트랜잭션</h3>
	<h1>트랜잭션_data1</h1>
	<br />
	<h3>DB주입 완.</h3>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<!-- SPRING + MYBATIS -->
	<h1>SPRING + MYBATIS</h1>
	<br />
	<h3>트랜잭션</h3>
	<h1>트랜잭션_data2</h1>
	<br />
	<h3>DB주입 완.</h3>
</body>
</html>

tx_data1,2 트랜잭션 에러없이 잘 처리되면 넘어갈 페이진데, 에러나게 해둬서 볼 일이 없는 페이지..

 

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<!-- SPRING + MYBATIS -->
	<h1>SPRING + MYBATIS</h1>
	<br />
	<h3>트랜잭션</h3>
	<h1>트랜잭션_error</h1>
	<br />
	<h3>Rollback 완.</h3>

</body>
</html>

얘만 보게 될 것이다... ^^

 

 

 

 

 

home으로 만들었던 index페이지에서 진짜 이동 되는지 한 번 해보자!

Run on Server를 클릭해주쟈

 

결과

728x90
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함