본문 바로가기
MyBatis

[MyBatis] MyBatis 세팅(2/2) + insert, update, delete

by moca7 2024. 10. 10.

 

 

ㅁ main.jsp를 아래와 같이 작성한다.

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h3>1. insert연습 (공지사항 등록)</h3>

    <h3>2. select연습 - 한 행 (공지사항 상세조회)</h3>
   
    <h3>3. select연습 - 여러행 (공지사항 전체목록조회)</h3>
   
    <h4>4. select연습 - 여러행 + 페이징 처리 - RowBounds 적용</h4>

    <h5>5. select연습 + update연습 (회원정보 상세조회 + 회원정보 변경) - TypeHandler 적용</h5>
   
    <h6>6. 동적쿼리 연습 (회원 검색)</h6>

</body>
</html>
 

 

- 스프링 넘어가서도 마이바티스는 활용하니 그때 더 많은 기능을 써보기로 한다.

 

 

 

 

 

ㅁ MyBatis 홈페이지에 접속한다.

- 마이바티스에서 제공하는 클래스들이 있다. 그 클래스들을 이용해서 CRUD를 진행한다.

그러기 위해서는 마이바티스 라이브러리가 필요하다.

 

 

 

 

- 프레임워크 공식 사이트는 항상 북마크한다. 

- 항상 이런 공식 사이트에는 get started(시작하기)가 있다.

 

 

 

 

 

 

- 마이바티스를 사용하기 위해서는 mybatis-x.x.x.jar 파일을 넣어야 한다. jar 파일이 곧 라이브러리다.

- 스프링에 들어가면 라이브러리를 관리하는 maven을 사용한다.

그럼 굳이 이 파일을 다운받아서 직접 넣을 필요는 없다.

maven 관련 문서에 저 dependency태그 5줄만 쓰면 된다.

 

- 지금은 직접 다운받는다. mybatis-x.x.x.jar를 누르면 깃으로 이동된다.

 

 

 

 

 



- x 부분은 버전이다. 항상 최신버전은 안받는게 좋다.

- 3.5.14 버전으로 한다. Assets를 눌러서 mybatis-3.5.14.zip파일을 받는다.

- 스프링 버전에 따라서 마이바티스는 몇버전을 써야 좋은 그런게 있다. 호환성이 다르다.

 

 

 

 

 

 

- zip파일의 압축을 풀고, mybatis-3.5.14.jar파일만 c:\dev와 src/main/webapp/WEB-INF/lib폴더에 가져다둔다. 

 

- 깃으로 프로젝트를 올릴 때 jar 파일은 안올라간다. jar 파일은 공유하지 않는다.

집에가면 jar 파일은 따로 받아야 한다. 구글드라이브 lib폴더에 사용하고있는 jar파일들 다 모여있다. 

 

 

 

 

 

 

ㅁ 다시 마이바티스의 시작하기 페이지로 돌아온다.

 

 

 

- 시작하기 페이지에서 아래로 내린다.

- 위의 화면에서 두번째 박스는 Java 코드고 세번재 박스는 xml 코드다. 

 

 

- 접속할 db에 대한 정보를 기존에는 driver.properties에 작성했는데 이제는 xml 파일에 작성한다.

- XML 파일은 MyBatis가 DB에 어떻게 연결할지 설정하는 파일이다.

DB 접속 정보(드라이버, URL, 사용자명, 비밀번호)와 트랜잭션 설정, 사용할 SQL 매퍼 파일 경로 등이 담겨 있어서, MyBatis가 DB와 상호작용할 수 있도록 도와주는 역할이다.

 

- 자바 코드는 XML 파일(MyBatis 설정 파일)을 읽어와서 데이터베이스에 연결(DB 연동)한다.

 

 

 

 

 

 

 

ㅁ mybatis-config.xml 파일을 만들어서 접속할 db에 대한 정보를 기술한다.

 

 

 

 

 

- 근데 저 파일은 "일반 폴더"가 아닌 "소스 폴더"에 위치해야 한다.

db환경설정 관련한 부분들은 소스폴더에 작성한다. 

- 지금 소스폴더는 src/main/java가 있는데 여기에 두지 않고 소스폴더를 새로만든다. resources라는 이름으로 만든다.

- resources라는 이름으로 만든 이유는 스프링 프로젝트하면 마이바티스 관련 환경설정 문서들을 보관할 폴더를 resources라는 이름으로 소스폴더가 자동으로 만들어진다.

 

 

 

 

 

 

 

- 프로젝트 아래에 바로 resources라는 소스폴더를 만든다.

소스폴더 resources 안에 "config"와 "mappers"라는 일반 폴더를 만든다.

 

- 환경설정 문서는 config 폴더에, mapper 파일(쿼리문)들은 mappers 폴더에 둘 예정이다.

 

 

 

 

 

 

 

ㅁ config 폴더 우클릭 - xml 파일 생성

 

 

- 다른 이름도 상관없는데 보통 mybatis관련 환경설정을 mybatis-config로 한다.

 

 

 

 

 

- XML 파일을 만들면 위와 같이 한 줄이 적혀있다.

- !DOCTYPE으로 문서 유형을 지정해줘야 한다.

 

- html 파일은 <!DOCTYPE html>, mapper 파일은 <!DOCTYPE property>를 작성했었다.

- mybatis-configuration은 <!DOCTYPE configuration>을 작성해야 한다. 

 

 

 

 

 

 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 
<configuration>


</configuration>  
 

 

- 공식 사이트에서 <!DOCTYPE으로 시작하는 3줄을 복붙한다.

- 2번째, 3번째 줄을 보면 dtd가 적혀있다. dtd는 문서 유형에 적법한 요소인지 유효성 체크를 한다.

이 문서 유형에 맞지 않는 태그를 사용하면 빨간줄로 오류를 알려준다.

- 3줄을 연이어서 1줄로 써도 된다. 

 

 

 

 

 

(1) configurtaion 태그

- 그리고 문서 유형에 맞는 최상위 태그가 필요하다.

- configuration이라는 태그로 감싼다. configuration 태그가 루트 element라는 최상위 태그다.

- 이 태그 안에 다른 태그들을 작성하면 된다.

 

 

 

 

(2) settings


    <settings>
        <setting name="jdbcTypeForNull" value="NULL" />
    </settings>
 

 

- 태그명이 복수 형태라는 말은 이 안에 단수 형태로 여러개의 <setting> 태그를 쓸 수 있다는 말이다.

 

- 자바에서의 null이 db에 들어가려고 하면 마이바티스에서는 오류가 난다.

null로 데이터가 전달되는 경우 DB에서도 null로 인식하게끔 한다.

- 자바에서 null이 전달되면, MyBatis가 그 값을 데이터베이스에서 허용하는 NULL 값으로 인식하도록 한다.

 

- name 속성값 "jdbcTypeForNull". 대소문자를 잘 구분해야 한다.

- value 속성값 "NULL". 꼭 대문자여야 한다. 아니면 DB 연동 자체가 안 됨. 

 

 

 

 

 

(3) typeAliases

 

   
    <typeAliases>
       
    </typeAliases>
   

 

- dto 클래스를 자주 제시할건데, 그때 클래스를 풀클래스명 대신에 별칭으로 지칭할 수 있도록 별칭을 등록하는 태그.

- 여러개의 <typeAlias> 태그를 쓸 수 있다.

 

 

 

 

(4) environments

 

	<environments default="development">
		<environment id="development">
			
		</environment>
		<environment id="production">
		
		</environment>
	</environments>

 

- 여러 개의 <environment> 태그를 쓸 수 있다.

 

- environments 태그는 연동할 db 정보를 등록하는 태그다.

- 첫번째 environments 태그에는 "개발단에서 사용할 db정보"를,

두번째 environments 태그에는 "운영단에서 사용할 db정보"를 작성한다.

 

- 각각의 environment 태그를 구분하기 위해서 얘네들을 지칭하는 id를 설정할 수 있다.

보통 개발단에서는 id를 development, 운영단에선 id로 production을 쓰곤 한다.

- 개발 서버와 운영 서버를 다르게 해서 각각 다른 db에 접속하게 할 수 있다.

개발할 때는 위를 쓰고 배포할때는 아래걸 쓰면 된다.

- 배포할 때는 default를 production으로 바꾸면 된다.

지금은 production에 대한 environments는 없어도 되서 지운다. 개발단에서 사용할 db정보만 작성한다.

 

- 상위 태그인 environments 태그에 default라는 속성에

여러개의 environment 중에서 어떤 id에 해당하는 envirionment와 연결할건지(어떤 db를 쓸건지) 작성할 수 있다.

- default 속성은 연결설정을 여러개 생성해두고 아이디로 구분하는데, 이때 기본적으로 연결시킬 id를 지정한다.

 

 

 

 

 


    <environments default="development">
   
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
                <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
                <property name="username" value="SERVER" />
                <property name="password" value="SERVER" />
            </dataSource>
        </environment>
       
    </environments>
 

 

 

※ envrionment 태그 안에 작성하는 태그들

 

 

(i) transactionManager

- type이라는 속성의 값으로 JDBC와 MANAGED를 쓸 수 있다.

- JDBC는 트랜잭션 관리를 개발자가 직접한다. (수동 COMMIT)

항상 수동 COMMIT이 좋다.

- MANAGED는 트랜잭션 관리를 개발자가 직접하지 않는다.

트랜잭션에 대해서 어떠한 영향도 행사하지 않는다. (AUTO COMMIT)

 

 

(ii) dataSource

- dataSource에 의해서 Connection 객체가 자동(내부적)으로 생성된다. 

 

- 하위 태그로 property 태그를 둔다. key-value 세트로 driver.properties에 작성했던 내용들을 기술한다.

- driver.properties에 작성할 때는 key값(name 속성값)을 맘대로 했지만

마이바티스는 key값(driver, url, username, password)이 정해져있다.

 

- dataSource 태그의 type 속성으로 POOLED를 준다.

- POOLED(사용), UNPOOLED(미사용) 중에서 선택할 수 있는데, ConnectionPool 사용 여부를 뜻한다.

- ConnectionPool은 Connection 객체를 담아둘 수 있는 영역이다.

DB에 접속할 때마다 매번 새로운 Connection 객체가 생성했다가 소멸하는 것은 좋지 않다.

한번 생성된 Connection 객체를 ConnectionPool에 담아두면 재사용이 가능하다. 쓰는게 좋다.

 

 

 

 

 

 

(5) mappers 태그

 

	<mappers>
		<mapper resource="mappers/notice-mapper.xml" />
		<mapper resource="mappers/member-mapper.xml" />
	</mappers>

 

 

- mappers 태그는 (실행할 sql문들이 기록되어있는) 앞으로 활용할 mapper 파일들을 등록하는 태그다.

- 이렇게 등록해 놓으면 dao측에서 어떤 mapper 파일의 어떤 쿼리를 실행할 건지 쉽게 지칭할 수 있다.

 

- 여러개의 mapper 파일들을 등록해 놓을 수 있다. namespace 속성값으로 구분한다.

 

 

 

 

 

 

※ mappers 태그에 등록할 mapper 파일 생성하기

 

 

 

- resources.mappers 패키지 안에 notice-mapper.xml과 member-mapper.xml을 만든다.

 

 

 

 

 

 

 

 

- 마이바티스 공식 사이트 - "매핑된 SQL 구문 살펴보기"에서 <!DOCTYPE으로 시작하는 3줄을 복사한다.

 

 

 

 

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="noticeMapper">

</mapper>

 

 

- notice-mapper.xml과 member-mapper.xml에 저 3줄을 붙여 넣는다.

- dtd로 유효성 체크를 한다.

- 이제부터 쿼리문을 작성하는 문서는 문서 유형을 "mapper"로 한다.

- 최상위 루트 엘리먼트는 mapper다.

 

- 이전에는 쿼리문을 작성하는 문서 유형이 "properties"였다.

- 최상위 루트 엘리먼트는 properties였고 그 안에 entry 태그를 작성했다.

 

- 마이바티스에서는 insert, update, select,  delete 태그로 다 다르게 작성해야 한다.

 

 

 

- namespace는 MyBatis가 해당 Mapper 파일의 SQL 쿼리를 구분하기 위한 고유한 이름이다.

- 각각의 mapper 파일들마다 무조건 namespace로 각각의 mapper파일을 지칭하는 이름이 등록되어있어야 한다. 

- 각각의 mapper 파일들의 mapper 태그의 namespace 속성값을 주지 않고 

mybatis-config.xml의 <mappers> 태그에 등록하면 오류난다. 

 

 

 

 

 

 

 

ㅁ 최종 mybatis-config.xml 

 

 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 
<configuration>

    <settings>
        <setting name="jdbcTypeForNull" value="NULL" />
    </settings>

   
    <typeAliases>
       
    </typeAliases>
   

    <environments default="development">
   
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="oracle.jdbc.driver.OracleDriver" />
                <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
                <property name="username" value="SERVER" />
                <property name="password" value="SERVER" />
            </dataSource>
        </environment>
       
    </environments>

   
    <mappers>
        <mapper resource="mappers/notice-mapper.xml" />
        <mapper resource="mappers/member-mapper.xml" />
    </mappers>



</configuration>  
 

 

 

- mybatis-config.xml 파일에 연결할 db 정보를 작성했다.

MyBatis 설정 파일을 읽어들여서 MyBatis용 객체를 생성하고, 그 객체로 쿼리문을 실행하고 트랜잭션 처리도 진행한다

 

- JDBCTemplate 클래스에 데이터베이스와 연결된 Connection 객체를 생성하고 반환하는 코드를 작성했었다.

그런식으로 mybatis-config.xml 문서를 읽어들이면서 db와 연결하는 구문을 Template 클래스에 작성한다.

 

 

 

==================================================================================

 

 

 

 

ㅁ  com.br.mybatis.common.template에 Template 클래스를 만든다.

 

 
package com.br.mybatis.common.template;

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class Template {

    /*
   
    기존의 JDBCTemplate 클래스
   
    public static Connection getConnection() {
        driver.properties 파일을 읽어들이면서
        해당 db와 접속된 Connection을 생성해서 반환
    }
   
    public static void close(JDBC용 객체들) {
        전달받은 JDBC용 객체들을 반납시키는 구문
    }
   
    public static void commit, rollback(Connection 객체) {
        트랜잭션 처리
    }
   
    */
 
 
   
   
    public static SqlSession getSqlSession() {
 
       
        SqlSession sqlSession = null;
       
        try {
            InputStream inputStream = Resources.getResourceAsStream("config/mybatis-config.xml");
            sqlSession = new SqlSessionFactoryBuilder().build(inputStream).openSession(false);
 
        } catch (IOException e) {
            e.printStackTrace();
        }
       
        return sqlSession;
       
    }
   
   
   
}
 

 

 

- 마이바티스를 사용하면 JDBC용 객체를 사용할 필요가 없다. 마이바티스에서 내부적으로 다 처리한다.

- 마이바티스용 객체로 SqlSession이라는 객체가 있다.

이 객체 하나로 쿼리 실행, 반납처리, 트랜잭션 처리도 가능하다.

 

- Template 클래스에는 mybatis용 객체인 SqlSession 객체를 생성해서 반환하는 메소드 하나만 둔다.

mybatis-config.xml 파일을 읽어들여서 해당 db와 접속된 sqlSession 객체를 생성해서 반환하는 코드를 작성한다.

 

- import문을 보면 java에서 제공하는 클래스가 아니다. 

org.apache.ibatis로 시작한다. 마이바티스가 아이바티스에서 넘어왔다.

 

 

 

 

 

 

 

- mybatis-config.xml을 읽어들이는 자바코드가 위의 3줄이다.

- 읽어들이고자 하는 파일의 일부경로를 가지고 inputStream 객체를 생성하고,

그걸 가지고 SqlSession 관련한 객체를 생성한다.

- 이 코드를 일부 참고해서 작성한다.

 

 

 

 

- 읽어들이려는 mybatis-config.xml 파일이 소스 폴더 안에 있다. 

- mybatis-config.xml 문서를 읽어들이기 위해서는 그 파일과 직접적으로 연결되는 InputStream 객체가 필요하다.

InputStream 객체를 먼저 생성한다. 

 

- Resources 클래스에 읽어들이고자 하는 파일의 일부 경로를 제공하면 그 파일을 쉽게 읽어들일 수 있다.

- Resources.getResourceAsStream() 메소드를 호출하면서 읽어들이고자 하는 파일의 일부 경로를 제시하면 소스 폴더 안에 있는 파일을 읽어들일 수 있다.

지금 resources라는 소스 폴더 안에 config 라는 폴더가 있고, 그 안에 mybatis-config.xml 파일이 있다.

읽어들이고자 하는 파일의 경로를 소스폴더로부터 찾을 수 있게끔 저렇게 일부경로를 제시하면 된다.

- 그러면 내부적으로 이 파일이 찾아지고 그 파일과 직접적으로 연결되는 InputStream 객체가 생성돼서 반환된다.

 

- SqlSession 객체를 생성하기 위해서는 SqlSessionFactory 객체가 먼저 필요하다.

SqlSessionFactory 객체를 생성하기 위해서는 SqlSessionFactoryBuilder 객체가 필요하다.

좀 복잡한데 굳이 알 필요는 없다. 저 3줄을 가지고 참고해서 쓰면 된다. 

- SqlSessionFactoryBuilder 객체가 제공하는 build() 메소드를 호출하면서 InputStream 객체를 전달하면 SqlSessionFactory 객체를 반환한다. 

- 반환받은 SqlSessionFactory 객체로부터 openSession이라는 메소드를 호출하면 SqlSession 객체를 반환받을 수 있다.

- openSession 메소드 호출시 false를 전달하면 auto commit을 안하겠다는 의미이다. 여기서도 써줘야 한다.

 

 

 

 

 

 

ㅁ 다시 main.jsp로 돌아온다.

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h3>1. insert연습 (공지사항 등록)</h3>

    <h3>2. select연습 - 한 행 (공지사항 상세조회)</h3>
   
    <h3>3. select연습 - 여러행 (공지사항 전체목록조회)</h3>
   
    <h4>4. select연습 - 여러행 + 페이징 처리 - RowBounds 적용</h4>

    <h5>5. select연습 + update연습 (회원정보 상세조회 + 회원정보 변경) - TypeHandler 적용</h5>
   
    <h6>6. 동적쿼리 연습 (회원 검색)</h6>

</body>
</html>
 

 

- main.jsp를 건들진 않고 확인만 함.

- 기능별로 서비스측에 어떤 메소드가 만들어져야 할지 미리 설계한다.

- 기능 분석이 끝났으면 그 기능을 처리하기 위해 서비스측에 어떤 메소드가 필요할지 예측이 가능해야 한다. 

(매개변수, 반환형 등)

 

 

 

 

 

ㅁ com.br.mybatis.notice.service 패키지에 NoticeService라는 인터페이스를 만든다. 

 

 
package com.br.mybatis.notice.service;

import java.util.List;

import com.br.mybatis.common.dto.PageInfoDto;
import com.br.mybatis.notice.dto.NoticeDto;

public interface NoticeService {

    // 공지사항 등록
    int insertNotice(NoticeDto n);
   
    // 공지사항 상세조회 (한 행 조회)
    NoticeDto selectNotice(int noticeNo);
   
    // 공지사항 목록조회 (페이징 x - 전체 게시글 조회)
    List<NoticeDto> selectList();
   
    // 공지사항 목록조회 (페이징 o)
    int selectListCount();
    List<NoticeDto> selectList(PageInfoDto pi);  // 오버로딩
   
}
 

 

 

- 그런데 서비스 클래스를 바로 만들지 않고 인터페이스를 만든다.

 

- 인터페이스도 클래스다. 단지 추상 메소드만 존재할 수 있는 클래스다.

- 인터페이스의 모든 메소드들에는 묵시적으로 public abstract가 붙는다.

 

 

 

 

 

 

ㅁ com.br.mybatis.notice.service에 NoticeServiceImpl 클래스를 만든다. 

 

 

 

 

 

- 인터페이스 구현 클래스를 만들 때, 'Add'를 눌러서 인터페이스를 검색하고 선택해서 implements된 채로 만들 수 있다.

 

 

 

 

 

 

 
package com.br.mybatis.notice.service;

import static com.br.mybatis.common.template.Template.getSqlSession;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

import com.br.mybatis.common.dto.PageInfoDto;
import com.br.mybatis.notice.dao.NoticeDao;
import com.br.mybatis.notice.dto.NoticeDto;

public class NoticeServiceImpl implements NoticeService {
   
   

    @Override
    public int insertNotice(NoticeDto n) {
        return 0;
    }
 
   
    @Override
    public NoticeDto selectNotice(int noticeNo) {
        return null;
    }

   
    @Override
    public List<NoticeDto> selectList() {
        return null;
    }

   
    @Override
    public int selectListCount() {
        return 0;
    }

   
    @Override
    public List<NoticeDto> selectList(PageInfoDto pi) {
        return null;
    }
   

}
 

 

 

- 인터페이스를 구현해서 미완성된 메소드들이 오버라이딩되어 있다. 반환형, 매개변수, 메소드명은 다 정해져있다.

- 각 기능에 맞게 서비스측 코드를 메소드 안에 채워 넣기만 하면 된다.

 

 

 

 

 

 

ㅁ com.br.mybatis.notice.dao에 NoticeDao를 만든다.

 

 
package com.br.mybatis.notice.dao;

public class NoticeDao {

}
 

 

 

 

 

ㅁ main.jsp 수정

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h3>1. insert연습 (공지사항 등록)</h3>
    <a href="${ contextPath }/regist.no">공지사항 등록페이지로 이동</a>

    <h3>2. select연습 - 한 행 (공지사항 상세조회)</h3>
   
    <h3>3. select연습 - 여러행 (공지사항 전체목록조회)</h3>
   
    <h4>4. select연습 - 여러행 + 페이징 처리 - RowBounds 적용</h4>

    <h5>5. select연습 + update연습 (회원정보 상세조회 + 회원정보 변경) - TypeHandler 적용</h5>
   
    <h6>6. 동적쿼리 연습 (회원 검색)</h6>

</body>
</html>
 

 

 

- a 태그로 공지사항 등록 페이지로 이동하게끔 한다.

그런데 이제 WEB-INF 폴더 안의 views폴더에 둔 페이지들은 무조건 서블릿을 통해서만 페이지 이동이 가능하다.

 

 

 

 

 

 

ㅁ com.br.mybatis.notice.controller에 NoticeRegistFormController를 만든다. 

 

 
package com.br.mybatis.notice.controller;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class NoticeRegistFormController
 */
@WebServlet("/regist.no")
public class NoticeRegistFormController extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public NoticeRegistFormController() {
        super();
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        request.getRequestDispatcher("/WEB-INF/views/notice/noticeRegistForm.jsp").forward(request, response);
       
       
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}
 

 

- 어노테이션을 /regist.no로 변경한다.

- 단순히 공지사항 등록페이지로 이동만 하는 서블릿이다.

 

 

 

 

 

ㅁ noticeRegistForm.jsp를 만든다.

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h2>공지사항 등록</h2>

    <form action="${ contextPath }/insert.no" method="post">
        제목 : <input type="text" name="title"> <br>
        내용 : <textarea name="content"></textarea> <br><br>

        <button type="submit">등록하기</button>
    </form>

</body>
</html>
 

 



- DB의 NOTICE 테이블을 보면 INSERT할 때 NOTICE_WRITER 컬럼(공지사항 글 작성자 번호)가 필요한데, 로그인 되어있다고 치고 1번회원이라고 가정한다. (현재 이 프로젝트에 로그인 기능이 구현되어 있지 않음)

 

 

 

 

 

 

com.br.mybatis.notice.controller에 NoticeInsertController라는 서블릿을 만든다.

 

 
package com.br.mybatis.notice.controller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.br.mybatis.notice.dto.NoticeDto;
import com.br.mybatis.notice.service.NoticeServiceImpl;

/**
 * Servlet implementation class NoticeInsertController
 */
@WebServlet("/insert.no")
public class NoticeInsertController extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public NoticeInsertController() {
        super();
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        NoticeDto n = new NoticeDto();
        n.setNoticeTitle(request.getParameter("title"));
        n.setNoticeContent(request.getParameter("content"));
        //n.setNoticeWriter(세션에 담겨있는 로그인한 회원의 회원번호);
        n.setNoticeWriter("1"); // 1번 회원이라고 가정한다.
       
        int result = new NoticeServiceImpl().insertNotice(n);
 
 
       
        if(result > 0) {
            System.out.println("공지사항 등록 성공");
            response.sendRedirect(request.getContextPath());
        }else {
            System.out.println("공지사항 등록 실패");
        }

       
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}
 

 

 

 

 

 

 

 

ㅁ NoticeServiceImpl를 수정한다.

 

 
package com.br.mybatis.notice.service;

import static com.br.mybatis.common.template.Template.getSqlSession;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

import com.br.mybatis.common.dto.PageInfoDto;
import com.br.mybatis.notice.dao.NoticeDao;
import com.br.mybatis.notice.dto.NoticeDto;

public class NoticeServiceImpl implements NoticeService {
 
   
   
    private NoticeDao noticeDao = new NoticeDao();
   


    @Override
    public int insertNotice(NoticeDto n) {
       
        SqlSession sqlSession = /*Template.*/getSqlSession();
        int result = noticeDao.insertNotice(sqlSession, n);
 
       
        if(result > 0) {
            sqlSession.commit();
        }
       
        sqlSession.close();
        return result;
    }

   
   
    @Override
    public NoticeDto selectNotice(int noticeNo) {
        return null;
    }

   
    @Override
    public List<NoticeDto> selectList() {
        return null;
    }

   
    @Override
    public int selectListCount() {
        return 0;
    }

   
    @Override
    public List<NoticeDto> selectList(PageInfoDto pi) {
        return null;
    }
   

}
 

 

 

- 잘 생각해보면 insert에 실패하면 들어간 데이터가 없기 때문에 rollback할 필요가 없다.

 

 

 

 

 

=====================================================================================

 

 

 

 

ㅁ 쿼리

 

- 마이바티스에서는 insert, update, select, delete 각각의 쿼리에 맞는 태그를 사용해야 한다.

- 각 태그에 id, parameterType, resultType, resultMap 4가지 주요 속성이 있다.

 

- sql문 실행시, 해당 sql문으로 전달되는 parameter로 부터 값을 뽑아서 채우고자 한다면 #{ } 또는 ${ }를 사용한다.

 

 

 

 

(1) id : 해당 sql문의 식별자 (dao측에서 실행할 sql문을 탐색할 때 사용됨)  

 

- ex) dao에서 "mapper파일의 namespace"."sql문의 id"를 제시해서 sql문 실행한다.

 

 

 

 

(2) parameterType : 해당 sql문으로 전달되는 파라미터의 타입 (생략가능)

 

- ex) parameterType="com.br.mybatis.notice.dto.NoticeDto" - 기본적으로 풀클래스명을 써야 함.

- ex) parameterType="NoticeDto" - alias를 등록한 경우에는 풀클래스명을 안 써도 됨.

 

- 전달된 파라미터가 내가 만든 dto 객체가 아니라 자바 기본 자료형일 수도 있다. 

- ex) parameterType="_int" <- mybatis 내장 별칭 (int가 전달되어도 _int라고 써야 함)

- ex) parameterType="string" <- mybatis 내장 별칭 (String이 전달되어도 string이라고 써야 함)

- ex) parameterType="map" <- mybatis 내장 별칭이 정해져 있다.

- 엄청 많은데 어차피 생략가능하다.

 

 

 

 

(3) resultType 

 

- select문에 작성하는 속성으로 반환값의 타입을 명시.

- insert, update, delete는 어차피 int 반환만 하기 때문에 반환값의 타입을 명시할 필요가 없다.

 

 

 

 

 

(4) resultMap

 

- db의 컬럼명과 담고자하는 dto 객체의 필드명을 따로 작성해서 매핑되도록 하는 태그.

 

 

 

 

 

 

 쿼리문의 실행 결과

- insert, update, delete는 처리 결과가 무조건 처리된 행 수인 int로 고정이다.

- select는 매번 결과에 대한 타입이 다르다.

count(*) 집계함수를 사용해서 처리된 행 수 하나만 조회될 수도 있다.

 

- ex) 조회된 값이 숫자 한개일 경우 자바의 int형으로 바로 반환하고 싶을 때 resultType="_int"
- ex) 조회된 값이 문자열 한개일 경우 자바의 String형으로 바로 반환하고 싶을때 resultType="string"

- ex) 조회된 한 행을 특정 dto 객체의 각 필드에 매핑해서 반환하고자 할 때
(단, 조회되는 컬럼들의 컬럼명이 담고자하는 dto객체의 필드명과 일치)
resultType="dto객체의타입"

 

- 마이바티스 공식 사이트 - 매퍼설정의 typeAliases 부분에서 MyBatis 내장 별칭들을 볼 수 있다.

 

 

 

 

 

 

 

※ sql문에서 전달된 parameter로 값을 설정하는 방법

 

 

(1) # { }

- 내부적으로 ?와 같은 역할을 수행한다. pstmt 방식으로 값을 처리한다. 

- #{ 전달된 dto객체의 필드 | 전달된 Map의 key | 전달된 변수명 }을 작성하면 값들이 꺼내진다.

- #{ }이 해당 값의 타입에 맞춰서 대체된다. (해당 값의 타입에 맞춰서 오라클에 반영된다)

 문자열이면 오라클에서의 문자열인 홑따옴표로 감싸진 채로 출력되고, 숫자면 따옴표 없이 그냥 출력된다. 

 

 

- ex) 전달된 dto객체의 name필드에 "홍길동", int age 필드에 10이라는 가정하에

 

where user_name = #{ name } and user_age = #{ age }        =>        where user_name = '홍길동' and user_age = 10

 

// pstmt.setString(1, name); pstmt.setInt(2, age); 과 같다.

 

 

 

 

(2) $ { }

- 값이 따옴표 없이 그대로 채워진다. 즉 문자열 데이터로 인식되지 않는다. 

- ${ }는 PreparedStatement를 사용하지 않는다.

?로 처리되지 않으며, 값이 SQL 쿼리문 안에 직접 삽입된다.

 

- sql문의 메타데이터(테이블명, 컬럼명, sql 자체 등)를 처리할 때 사용한다. 

쿼리문 작성할 때 메타데이터(테이블 명이나 키워드, 컬럼명)에는 따옴표를 쓰지 않는다. 

메타데이터들을 채울 때 ${ }를 사용한다.

- 따옴표나 이스케이프 문자없이 그대로 출력된다. 

 

 

- ex) 전달된 변수 String table에 "member"라는 문자열 데이터가 있었을 경우

 

select * from ${ table }        =>        select * from member

 

 

 

====================================================================================

 

 

 

ㅁ notice-mapper.xml을 수정한다.

 

 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<mapper namespace="noticeMapper">

    <insert id="insertNotice" parameterType="com.br.mybatis.notice.dto.NoticeDto">
        insert
          into notice
          (
            notice_no
          , notice_title
          , notice_content
          , notice_writer
          )
          values
          (
            seq_nno.nextval
          , #{ noticeTitle }
          , #{ noticeContent }
          , #{ noticeWriter }
          )      
    </insert>


</mapper>
 

 

 

- insert 태그의 id를 메소드명과 동일하게 작성했다.

- 사실 parameterType 속성은 없어도 실행이 잘 된다. 옛날에는 필수였겠지만 지금은 옵션이다.

 

- #{ noticeTitle }이 pstmt.setString(1, n.getNoticeTitle());이다.

- #{ noticeContent }이 pstmt.setString(2, n.getNoticeContent());이다.

- #{ noticeWriter }이 pstmt.setString(3, n.getNoticeWriter());이다.

 

- 이제부터 sql문 작성할 때 물음표를 쓰지 않는다.

물음표를 채우기 위한 객체를 전달받고, 어떤 필드값을 꺼낼건지를 애초에 작성할 예정이다.

 

 

 

 

 

 

ㅁ NoticeDao를 수정한다.

 

 
package com.br.mybatis.notice.dao;

import org.apache.ibatis.session.SqlSession;

import com.br.mybatis.notice.dto.NoticeDto;

public class NoticeDao {

    public int insertNotice(SqlSession sqlSession, NoticeDto n) {
       
        return sqlSession.insert("noticeMapper.insertNotice", n);
    }

}
 

 

 

- 길었던 dao의 코드가 1줄로 줄어들었다. 

- 이제부터 sql문을 작성할 때 물음표를 사용하지 않는다.

 

- select는 한 행 조회면 selectOne 메소드, 여러행 조회면 selectList 메소드가 있다.

selectList 메소드는 3종류가 있다. (완성된 sql문, 미완성된 sql문, 페이징처리 RowBounds rowBounds)

 

- insert, update, delete 메소드는 매개변수 하나짜리 메소드와 매개변수 두개짜리 메소드가 있다.

- 이미 완성된 sql문이면 n을 전달할 필요가 없다. 그냥 매개변수 하나짜리 insert 메소드를 사용하면 된다.

- 완성되지 않은 sql문이면 두번째 인자값으로 Object parameter를 전달한다.

- insert, update, delete 메소드들은 두 종류의 메소드 다 int를 반환한다. 그 int를 리턴한다.

- 첫번째 인자값으로는 현재 등록되어있는 매퍼 파일들 중에서 어떤 매퍼파일의 어떤 id를 가진 sql문을 실행할 건지 쓴다.

 

 

 

 

 

 

 

ㅁ insert 기능 사용해보기

 

 

- 제목과 내용을 작성하고 등록하기를 누른다.

 

 

 

 

- 메인페이지로 돌아온다. 

 

 

 

 

 

 

 

- 콘솔에도 뜨고 db에도 insert가 되었다.

 

 

 

============================================================================

 

 

 

[ 연습 update ]

 

 

 

ㅁ  notice-mapper.xml에 id가 updateNotice인 update 태그를 만든다.

 

 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<mapper namespace="noticeMapper">

    <insert id="insertNotice" parameterType="com.br.mybatis.notice.dto.NoticeDto">
        insert
          into notice
          (
            notice_no
          , notice_title
          , notice_content
          , notice_writer
          )
          values
          (
            seq_nno.nextval
          , #{ noticeTitle }
          , #{ noticeContent }
          , #{ noticeWriter }
          )      
    </insert>

    <update id="updateNotice" parameterType="com.br.mybatis.notice.dto.NoticeDto">
        update
                notice
           set
                notice_title = #{ noticeTitle }
              , notice_content = #{ noticeContent }
         where
                notice_no = #{ noticeNo }              
    </update>


</mapper>
 

 

 

- 사실 parameterType은 없어도 된다. 나중엔 생략할 예정이다.

 

 

 

 

 

ㅁ NoticeDao에 updateNotice()를 만든다.

 

 
package com.br.mybatis.notice.dao;

import org.apache.ibatis.session.SqlSession;

import com.br.mybatis.notice.dto.NoticeDto;

public class NoticeDao {

    public int insertNotice(SqlSession sqlSession, NoticeDto n) {
       
        return sqlSession.insert("noticeMapper.insertNotice", n);
    }
   
    public int updateNotice(SqlSession sqlSession, NoticeDto n) {
       
        return sqlSession.update("noticeMapper.updateNotice", n);
       
    }

}
 

 

 

 

 

ㅁ dao와 쿼리문만 연습차원에서 하나씩 더 써봤다.

 

 

 

===================================================================================

 

 

 

[ 연습 delete ]

 

 

 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<mapper namespace="noticeMapper">

    <insert id="insertNotice" parameterType="com.br.mybatis.notice.dto.NoticeDto">
        insert
          into notice
          (
            notice_no
          , notice_title
          , notice_content
          , notice_writer
          )
          values
          (
            seq_nno.nextval
          , #{ noticeTitle }
          , #{ noticeContent }
          , #{ noticeWriter }
          )      
    </insert>

    <update id="updateNotice" parameterType="com.br.mybatis.notice.dto.NoticeDto">
        update
                notice
           set
                notice_title = #{ noticeTitle }
              , notice_content = #{ noticeContent }
         where
                notice_no = #{ noticeNo }              
    </update>

    <delete id="deleteNotice" parameterType="_int">
        delete
          from notice
         where notice_no = #{ noticeNo }
    </delete>


</mapper>
 

 

- 글번호 int를 받는다. parameterType의 속성값은 "_int"다. 생략 가능.

 

 

 

 
package com.br.mybatis.notice.dao;

import org.apache.ibatis.session.SqlSession;

import com.br.mybatis.notice.dto.NoticeDto;

public class NoticeDao {

    public int insertNotice(SqlSession sqlSession, NoticeDto n) {
       
        return sqlSession.insert("noticeMapper.insertNotice", n);
    }
   
    public int updateNotice(SqlSession sqlSession, NoticeDto n) {
       
        return sqlSession.update("noticeMapper.updateNotice", n);
       
    }
   
    public int deleteNotice(SqlSession sqlSession, int noticeNo) {
       
        return sqlSession.delete("noticeMapper.deleteNotice", noticeNo);
    }

}
 

 

- 마이바티스에서는 int라고 쓸수 없고 _int라고 써야한다. 물론 parameterType 속성 자체를 안써도 된다.

- #{ }에 변수명을 쓰면 변수에 담겨진 값이 알아서 꺼내진다.