«   2025/06   »
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
Tags more
Archives
Today
Total
관리 메뉴

장미의 개발일기

Java와 DB 연결 (JDBC) / MyBatis(마이바티스를 사용하는 이유) 본문

개발일기/JSP 프로그래밍 (Java 웹프로그래밍)

Java와 DB 연결 (JDBC) / MyBatis(마이바티스를 사용하는 이유)

민장미 2023. 6. 22. 22:24

- DB 와 서버 구조

* 2tier : 드라이버를 통해서 직접 DB에 접속하는 방식.  물론 이 방식 안 쓴다...

  

*3tier :  서버와 DB 사이에 미들웨어가 있는 구조 

*미들웨어: 양쪽을 연결하여, 데이터를 주고 받을 수 있도록, 중간에서 매개 역할을 하는 소프트웨어

*WAS는 서블릿 컨테이너로 톰캣이라고 생각하면 된다. WAS는 서블릿을 관리하는데, WAS 시점에서 보면

JSP도 서블릿과 같다. 

 

JDBC 란? =>  Java Data Base Connectivity.  자바와 DB를 연결하여 프로그래밍을 하기 위한 라이브러리다. 

jdbc url =>  jdbc:oracle:thin:@localhost:1521:orcl  (암기추천)

 

 

 

 

* Java와 DB 연결 과정:  (jdbc드라이버가 있어야 하며, 오라클 설치가 되어 있어야한다)

 

아래는 자바와 DB 연결 코드다  웹까지 연결은 안 되는 코드니 참고!   위의 그림과 같이 보기 추천 

package ex1;

import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;

//Connection 객체를 사용해서 오라클에 접속 테스트
public class OracleConnectionTest {
	public static void main(String[] args) {
		
		
		Scanner sc = new Scanner(System.in);
		
		System.out.print("작성자:");
		String writer = sc.nextLine();
		System.out.print("제목:");
		String subject = sc.nextLine();
		
		// if esle => 1번이면 파일로 저장, 2번이면 디비로 저장  구현해보기 
		
		//1. 드라이버를 로딩
		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");
			System.out.println("드라이버 로딩 성공!");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			//oracle.jdbc.driver 
		}
		//2. 데이터베이스 연결 테스트
		Connection con = null;
		// protocol(통신규약) : jdbc: oracle:thin:      //sftp,  pop3, 등 규약
		// =>@host:port:sid
		//port는 바뀔 수 있다. LSNRCTL로 cmd에서 알수있다.  
		String url = "jdbc:oracle:thin:@localhost:1521:orcl"; //무조건 외우기
		String user = "아이디적기";
		String pass = "비번적기";
		
		
		
		try {
			con = DriverManager.getConnection(url, user, pass);
			System.out.println("ConnetionTest:"+con);
			//  insert into memo values(memo_seq.nextVal,'김길동','하이',sysdate);
			//2. 사용자로부터 입력값을 받아서 memo란 테이블에 데이터르르 저장하는 명령문을 
			// 디비에 전송 
			String sql = "insert into memo values(memo_seq.nextVal, ? , ? ,sysdate)";
			//? 1번 ? 2번  ? 끼리의 순서가 중요
			PreparedStatement pstmt = con.prepareStatement(sql); //커넥션에 연결된 ? 
			// 입력받은 변수 2개 writer, subject 를 ?에 순서대로 연결한다. (binding 한다)
			pstmt.setString(1, writer);
			pstmt.setString(2, subject);   //2번째 물으뮤ㅛ
			//이제 전송을 실행한다.
			pstmt.executeUpdate(); //DML용 
			// 실행 후에 cmd에서 체크 (적용된거)
		} catch (SQLException e) {
			// SQLException ** 고객에게는 안보여줘도 되지만 개발단계에선 절대 없애선 안된다. *****
			e.printStackTrace();
		}finally {
			try {
				if(con != null) {
				con.close();}
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} //테스트 후에는 반드시 연결을 끊어줘야한다. ***
		}
	}

}

 

 

 

 

 

 

 

jdbc api 클래스 구조 참고

* 핵심 키워드: 드라이버를 로드하여, connetion 객체를 생성, connetion 객체를 통해 preparedstatement 객체를 생성. 

preparedstatement 객체를 통해 sql문을 실행 및 바인딩.

select문일 경우, ResultSet 객체(cursor)를 통해  데이터를 받아온다.

 

*preparedstatement 만 사용할 것! statement 객체는 보안이 약하다.. 게다가 동적 바인딩이 안된다.  절대 사용 ㄴㄴ 

* sql문을 sql 툴에서 테스트 하고, 이클립스로 가져올 때,  ';' 세미콜론을 반드시 제거해야한다는 점을 기억해두자. 스프링, 마이바티스에서도 해당 된다. 

 

 

 

-----본인 셋팅:  이클립스EE, 오라클 11g , 톰캣9.0, sql gate  

 

 

오라클설치하는 포스팅을 하고 싶은데.... 컴에는 무사히 설치 됐지만 , 노트북 날려먹고 설치했었어서, 

긴장 엄청한 상태로 설치해서 남긴 기록이 없다 ㅠㅠ

( 참고로 오라클은 한번에 잘 설치해야한다. 완전히 지우기도 힘들어서 다시 깔려고 해도 오류 많이 난다고 함)

 

 

오라클 얘네가 이제 옛날버전 다 숨겨둬서;; 필요하신 분 있으면 버전 6 받아가세요~

ojdbc6.jar
3.52MB

 

 

자 그럼 마이바티스를 사용해보자~~

 

 

마이바티스 다운로드 및 셋팅 포스팅 : 

Mybatis 마이바티스 다운로드 및 환경 설정 (tistory.com)

 

Mybatis 다운로드 및 환경 설정

** 이미 model 1 타입의 게시판 사이트를 제작한 후, 그걸 복사해와서 테스트 해보는 포스팅 입니다. ** model1 타입 웹개발 (게시판 만들기) 포스팅 : web java: 모델1 방식의 웹 개발하기(게시판 만들기)

jangmicoding.tistory.com

 

 

마이바티스는 DB관련 작업을 엄청 수월하게 해준다.  

전에 포스팅 했었던, JSP 모델1방식으로 게시판 만들기 예제를 통해서 비교해보자 !

 

JSP 프로그래밍: 모델1 방식의 웹 개발하기(게시판 만들기) - 1 FBoard (tistory.com)

 

JSP 프로그래밍: 모델1 방식의 웹 개발하기(게시판 만들기) - 1 FBoard

*Model 1 방식: JSP 프로그래밍에서 Model과 View를 따로 분리하지 않고 같이 섞어서 개발하는 방식이다. => UI(디자인)와 비즈니스 로직을 함께 처리하는 방식이라 코드의 재사용성이 떨어진다 => 유지

jangmicoding.tistory.com

JSP 프로그래밍: 모델1 방식의 웹 개발하기(게시판 만들기) - 2 FBoard (tistory.com)

 

JSP 프로그래밍: 모델1 방식의 웹 개발하기(게시판 만들기) - 2 FBoard

모델 1 방식은 이제는 거의 안 쓰고 매우 번거로운 과정이지만 개념이해를 위해서 반드시 코딩을 해봐야 한다. 그 다음으로 모델2 방식, 스프링 프레임워크, 부트 순으로 넘어가면 된다. 이번 포

jangmicoding.tistory.com

 

 

차이점 

 

1. 

 

마이바티스 미사용 => 클래스를 생성하고, 그 클래스에 DataSource 객체를 통해 Connetion 객체를 생성한다. 

package conn;

import java.sql.Connection;
import java.sql.SQLException;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class MyConn {
	// context.xml에 작성한 context를 읽어와서
	// DataSource 객체를 획득해서 Connection을 반환하는 메서드를 정의하는 것이 목적 
	
	private static DataSource ds;
	
	
	static {
		try {
			// context.xml에 Resource 객체를 통해서
			// jndi 방식: context.xml에 있는 name= "jdbc/myora" lookup이란 메서드로 검색
			/* JNDI(java Naming and Directory Interface)는
			 * Was 단에 데이터베이스 커넥션 객체를 미리 네이밍 해두는 방식(context.xml)이다.
			 * Connetion Pool에 Connection을 가져와서 Connection으로 반환하는 객체
			 * 즉, DataSource의 이름을 검색하는 방식이다.
			 * 사전적으로는 디렉토리 서비스에서 제공하는 데이터 및 객체를 발견하고
			 * 참고하기 위한 자바 API라고 한다. 
			 */
			
			InitialContext ctx = new InitialContext();
			ds =(DataSource) ctx.lookup("java:comp/env/jdbc/myora"); //외우기
		} catch (NamingException e) {
			e.printStackTrace();
		}
	}
	
	
	public static Connection getConn() throws SQLException{  //connection: sql관련 interface
		return ds.getConnection(); 
	}
	
	

}

 

 

마이바티스 사용  =>  

config.xml (매퍼 등록작업)

<?xml version="1.0" encoding="UTF-8" ?>
<!-- src/config/config.xml -->
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="">
    <environment id="">
      <transactionManager type="JDBC"/>
      <dataSource type="JNDI">
        <property name="data_source" value="java:comp/env/jdbc/myora"/>
      </dataSource>
    </environment>
  </environments>
<!-- 작업할 매퍼는 꼭 등록 해줘야 한다!!! -->   
<mappers>
	<mapper resource="mapper/tboard.xml"/>
	<mapper resource="mapper/pororo.xml"/>
</mappers>
</configuration>

 

 

context.xml (파일 이름 절대 틀리면 안된다) 를 다이나믹 웹 프로젝트를 생성한 후에, META-INF 폴더에 저장한다. 

<?xml version="1.0" encoding="UTF-8"?>
<Context>  <!-- myora는 내가 지어줄수있지만, jdbc는 바꾸면안됌 -->
	<Resource name="jdbc/myora" 
		auth="Container"
		type="javax.sql.DataSource"
		driverClassName="oracle.jdbc.driver.OracleDriver"
		url="jdbc:oracle:thin:@localhost:1521:orcl"
		username=""
		password=""
		maxActive="20"
		maxIdle="10"
		maxWait="-1"
/>
</Context>

위의 context.xml 코드를 보자.  

대충 원리,과정을 설명하자면 자바에서 DB를 미리 연결해둔다는 뜻이다. (=>connetion 객체를 얻기 위한 과정)

 

CP(Connetion Pool)에서 JNDI(Java Naming and Directory Interface) 방식으로  (name:jdbc/myora) 

DataSource 객체를 호출한다. 그 DataSoource 객체를 통해 Connetion 객체를 가져와 DB, 자바와의 연결에 사용한다. 

*DataSource: DB의 연결 설정정보를 포함하는 객체 

 

위의 마이바티스 미사용 방법에서는 드라이버를 연결때마다 로드하여 연결객체를 생성해줘야하는데, 마이바티스에선 

연결을 미리 해두는 방식을 사용하여 실행 속도를 높여준다. 

 

 

 

 

2. DAO 코드 

 

마이바티스 미사용  

package dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.sun.net.httpserver.Authenticator.Result;

import conn.MyConn;
import vo.FBoardVO;

public class FBoardDao implements FBoardDaoInter {

	// 싱글톤 : 자원이 제한적일때 스레드간의 간섭을 피하면서
	// 자원을 효율적으로 사용하기 위한 패턴이다.
	// 싱글톤 처리 영역
	// static 영역에 오직 하나만 생성하고 , 다른객체가 공유하는 개념
	private static FBoardDao dao;
	// 외부에서 생성되지 않고 getDao()메서드를 호출 시에 최초에 한번만 생성하고
	// 다음부터는 이미 생성된 주소를 사용하자!

	private FBoardDao() {
	}

	public static FBoardDao getDao() {
		if (dao == null) {
			dao = new FBoardDao();
		}
		return dao;
	}

	// 싱글톤 처리 영역

	// 이 메소드로 테스트
	@Override
	public void addFBoard(FBoardVO vo) {
		/*
		 * System.out.println("reip:"+vo.getReip());
		 * System.out.println("subject:"+vo.getSubject());
		 * System.out.println("writer:"+vo.getWriter());
		 * System.out.println("content:"+vo.getContent());
		 * System.out.println("pwd:"+vo.getPwd());
		 */
		// 이거 작성후 다시 폼에 입력후 체크
		// SUBJECT, WRITER, PWD, CONTECNT, HIT, REIP, FDATE
		// INSERT INTO FBOARD VALUES(FBOARD_SEQ.NEXTVAL, ?,?,?,?, 0, ?,SYSDATE)
		String sql = "INSERT INTO FBOARD VALUES(FBOARD_SEQ.NEXTVAL, ?,?,?,?, 0, ?,SYSDATE)";

		try (Connection con = MyConn.getConn(); PreparedStatement pstmt = con.prepareStatement(sql);) {
			// 바인딩하기 제목, 작성자, 비번, 내용, ip ?순서임
			pstmt.setString(1, vo.getSubject());
			pstmt.setString(2, vo.getWriter());
			pstmt.setString(3, vo.getPwd());
			pstmt.setString(4, vo.getContent());
			pstmt.setString(5, vo.getReip());

			pstmt.executeUpdate();

		} catch (SQLException e) {
			e.printStackTrace();
		}

	}// add fboard

	@Override // DB에 저장된 데이터를 출력해주는 메서드 구현
	public List<FBoardVO> listFBoard() {
		String sql = "SELECT NUM, SUBJECT, WRITER, HIT, FDATE FROM FBOARD ORDER BY 1 DESC";
		List<FBoardVO> flist = new ArrayList<FBoardVO>(); // 출력할 정보를 담아올 리스트객체

		try (Connection con = MyConn.getConn(); PreparedStatement pstmt = con.prepareStatement(sql);) // sql문을 statement
																										// 객체에 담아서
		{
			ResultSet rs = pstmt.executeQuery(); // resultset에 select문 결과물을 저장

			while (rs.next()) { // rs의 객체에 있는 모든 정보를 출력 /반복문
				FBoardVO v = new FBoardVO();
				v.setNum(rs.getInt("num"));
				v.setSubject(rs.getString("subject"));
				v.setWriter(rs.getString("writer"));
				v.setHit(rs.getInt("hit"));
				v.setFdate(rs.getString("fdate"));
				flist.add(v);
			} // wh
		} catch (Exception e) {
			e.printStackTrace();
		}
		return flist;
	}

	@Override
	public void updateHit(int num) {
		String sql = "UPDATE FBOARD SET HIT = HIT+1 WHERE NUM=?";
		// 인서트 메소드에서 복붙

		try (Connection con = MyConn.getConn(); PreparedStatement pstmt = con.prepareStatement(sql);) {
			pstmt.setInt(1, num);
			pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	@Override
	public FBoardVO detailFBoard(int num) {
		String sql = "SELECT NUM,SUBJECT,WRITER,PWD,CONTENT,HIT,REIP,FDATE FROM FBOARD WHERE NUM=?";

		FBoardVO v = new FBoardVO();

		try {
			Connection con = MyConn.getConn();
			PreparedStatement pstmt = con.prepareStatement(sql);
			// 바인딩
			pstmt.setInt(1, num);
			ResultSet rs = pstmt.executeQuery();

			// resultset에 select문 결과물을 저장

			if (rs.next()) { // rs의 객체에 있는 모든 정보를 출력 /반복문
				v.setNum(rs.getInt("num"));
				v.setSubject(rs.getString("subject"));
				v.setWriter(rs.getString("writer"));
				v.setPwd(rs.getString("pwd"));
				v.setContent(rs.getString("content"));
				v.setHit(rs.getInt("hit"));
				v.setReip(rs.getString("reip"));
				v.setFdate(rs.getString("fdate"));
			} // if

		} catch (Exception e) {
			e.printStackTrace();
		}
		return v;
	}

	@Override
	public boolean checkPwd(FBoardVO vo) {
		String sql = "SELECT COUNT(*) AS CNT FROM FBOARD WHERE NUM=? AND PWD = ?";

		int res = 0;
		try {
			Connection con = MyConn.getConn();
			PreparedStatement pstmt = con.prepareStatement(sql);
			// 바인딩
			pstmt.setInt(1, vo.getNum());
			pstmt.setString(2, vo.getPwd());
			ResultSet rs = pstmt.executeQuery();

			// resultset에 select문 결과물을 저장

			if (rs.next()) { // rs의 객체에 있는 모든 정보를 출력 /반복문
				res = rs.getInt("cnt");
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		if (res > 0) {// 1값 비밀번호가 맞는 경우,
			return true;
		} else {
			return false;
		}
	}// method

	@Override
	public void deleteFBoard(int num) {
		String sql = "DELETE FROM FBOARD WHERE NUM=?";
		// 업데이트 메소드에서 복붙

		try (Connection con = MyConn.getConn(); PreparedStatement pstmt = con.prepareStatement(sql);) {
			pstmt.setInt(1, num);
			pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}


	@Override
	public void updateFBoard(FBoardVO vo) {
		//ADD BOADDF 복붙 
		String sql = "UPDATE FBOARD SET SUBJECT=?,WRITER=?, PWD=?, CONTENT=?, REIP=? WHERE NUM=?";

		try (Connection con = MyConn.getConn(); PreparedStatement pstmt = con.prepareStatement(sql);) {
			// 바인딩하기 제목, 작성자, 비번, 내용, ip ?순서임
			pstmt.setString(1, vo.getSubject());
			pstmt.setString(2, vo.getWriter());
			pstmt.setString(3, vo.getPwd());
			pstmt.setString(4, vo.getContent());
			pstmt.setString(5, vo.getReip());
			pstmt.setInt(6, vo.getNum());

			pstmt.executeUpdate();

		} catch (SQLException e) {
			e.printStackTrace();
		}

	}// mt

}

 

 

위에서는 PreparedStatement 객체를 통해서 sql문을 실행하지만, 

마이바티스 사용하면  => mapper xml 파일에서 sql문을 작성하고  필요한 메소드에 매핑해줌으로써 DAO 코드를 훨씬

간단하게 작성할 수가 있다. /////SqlSession 객체는 마이바티스(MyBatis)에서 제공해주는 객체로

auto commit()과 auto close() 기능을 제공한다. 

 

<?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="tboard">
	<insert id="add" parameterType="vo.FBoardVO">
	INSERT INTO FBOARD VALUES(FBOARD_SEQ.NEXTVAL, #{subject},#{writer},#{pwd},#{content}, 0, #{reip},SYSDATE) 
	</insert>
	<select id="list" resultType="vo.FBoardVO"> 
	SELECT * FROM FBOARD ORDER BY 1 DESC
	</select>
	<update id="hit" parameterType="int">
	UPDATE FBOARD SET HIT = HIT+1 WHERE NUM=#{num}
	</update>
	<select id="detail" parameterType="int" resultType="vo.FBoardVO">
	SELECT NUM,SUBJECT,WRITER,PWD,CONTENT,HIT,REIP,FDATE FROM FBOARD WHERE NUM=#{num}
	</select>
	
	<select id="chkpwd" parameterType="vo.FBoardVO" resultType="int">
	SELECT COUNT(*) FROM FBOARD WHERE NUM=#{num} AND PWD=#{pwd}
	</select>
	
	<delete id="del" parameterType="int">
	DELETE FROM FBOARD WHERE NUM=#{num}
	</delete>
	
	<update id="update" parameterType="vo.FBoardVO">
	UPDATE FBOARD SET SUBJECT=#{subject},WRITER=#{writer}, PWD=#{pwd}, CONTENT=#{content}, REIP=#{reip} WHERE NUM=#{num}
	</update>
	
</mapper>
package dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.apache.ibatis.session.SqlSession;

import com.sun.net.httpserver.Authenticator.Result;

import factory.FactoryService;
import vo.FBoardVO;

public class FBoardDao implements FBoardDaoInter {

	private static FBoardDao dao;

	private FBoardDao() {
	}

	public static FBoardDao getDao() {
		if (dao == null) {
			dao = new FBoardDao();
		}
		return dao;
	}

	public void addFBoard(FBoardVO vo) {
		//sqlsession 객체를 가지고 사용**
		SqlSession ss = FactoryService.getFactory().openSession();
		//입력처리 => insert()호출
		ss.insert("tboard.add",vo);  //namspace.id 
		ss.commit();
		ss.close();
	}// add fboard

	
	@Override
	public List<FBoardVO> listFBoard() {
		SqlSession ss = FactoryService.getFactory().openSession();
		//selectList()를 호출하면 해당 쿼리의 결과를 List로 저장한다. 
		List<FBoardVO> list = ss.selectList("tboard.list");
		ss.close();
		return list;
	}

	@Override
	public void updateHit(int num) {
		SqlSession ss = FactoryService.getFactory().openSession();
		ss.update("tboard.hit",num);
		ss.commit();
		ss.close();
	}//hit

	@Override
	public FBoardVO detailFBoard(int num) {
		SqlSession ss = FactoryService.getFactory().openSession();
		///쿼리 결과과 단일행 일 경우 : select_one
		FBoardVO v = ss.selectOne("tboard.detail",num);
		ss.close();
		return v;
	}//detail
	

	@Override
	public boolean checkPwd(FBoardVO vo) {
		SqlSession ss = FactoryService.getFactory().openSession();
		
		int res =ss.selectOne("tboard.chkpwd", vo);
		ss.close();
		if(res > 0 ) {
			return true;
		}else {
			return false;
		}
	} //chk
	

	@Override
	public void deleteFBoard(int num) {
		SqlSession ss = FactoryService.getFactory().openSession();
		ss.delete("tboard.del",num);
		ss.commit();
		ss.close();
	}

	@Override
	public void updateFBoard(FBoardVO vo) {
		SqlSession ss = FactoryService.getFactory().openSession();
		ss.update("tboard.update",vo);
		ss.commit();
		ss.close();
	}
	
	
}