Post

[Java] DB 연동

이번시간에는 oracle이나 mysql로 작업했던것을 java와 연동하여 java에서 db 수정을 해보고 db에 있는 데이터들을 가져오겠습니다!

먼저 java에서 오라클데이터베이스에 있는 자료를 추가해보겠습니다!

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package com.koreait.db1;

import java.awt.FlowLayout;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;

//원격지에 떨어진, 오라클 데이터베이스에 DML작업을 수행해보자!
public class OracleApp extends JFrame implements ActionListener {
	JTextField book_name;
	JTextField price;
	JButton bt_regist;
	Connection con; // 모든 인스턴스 메서드가 접근할 수 있도록 멤버변수로 뺐다!

	public OracleApp() {
		// 생성
		bt_regist = new JButton("등록");
		book_name = new JTextField(25);
		price = new JTextField(25);

		// 스타일
		setLayout(new FlowLayout());

		// 조립
		add(book_name);
		add(price);
		add(bt_regist);

		// 윈도우 설정
		setSize(450, 150);
		setVisible(true);
		// setDefaultCloseOperation(EXIT_ON_CLOSE);// 윈도우 창닫기를 누르면, 현재 프로그램을 종료..

		// 오라클 접속
		connect();

		// 연결
		bt_regist.addActionListener(this);
		this.addWindowListener(new WindowAdapter() {
			// 사용자들이 윈도우 닫기 버튼을 누르면..
			public void windowClosing(WindowEvent e) {
				// db와 관련된 자원을 해제..
				try {
					con.close();
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
				System.exit(0);// 현재 프로그램 종료
			}

		});

	}

	// 쿼리 실행에 앞서, 오라클서버에 먼저 접속을 성공해야 한다..
	public void connect() {
		// 오라클 드라이버를 클래스 로드!!
		try {// 로드하고 싶은 클래스의 경로..
			Class.forName("oracle.jdbc.driver.OracleDriver");
			System.out.println("드라이버 로드 성공");
			// 접속할 때 사용하는 JDBD객체는 DriverManager이다!!
			con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE", "batman", "1234");

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(this, "드라이버를 찾을 수 없습니다.");
		} catch (SQLException e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(this, "SQL에 접속할 수 없습니다.");
		}

	}

	public void insert() {
		// 이미 로그인을 성공한 상태이므로, 쿼리문을 수행할 수 있다
		// 쿼리문을 수행하기 위한 인터페이스가 바로 PreparedStatement 객체

		String name_value = book_name.getText();// 입력한 책의 이름
		int price_value = Integer.parseInt(price.getText());// String -->int

		String sql = "insert into book(book_id, book_name, price) values(seq_book.nextval, '" + name_value + "', "
				+ price_value + ")";

		PreparedStatement pstmt = null;// 멤버가 아닌 지역변수는 변수 선언시 초기화 되어있어야한다.

		try {
			if (con != null) {
				JOptionPane.showMessageDialog(this, "접속성공");

				// 이미 로그인을 성공한 상탵이므로, 쿼리문을 수행할 수 있다!!
				// 쿼리문을 수행하기 위한 인터페이스가 바로 PreparedStatement 객체
				pstmt = con.prepareStatement(sql);// 인스턴스를 만들기만 했음, 수행은 안했다..

				int result = pstmt.executeUpdate();// DML(insert,update,delete)수행메서드
				if (result > 0) {
					JOptionPane.showMessageDialog(this, "등록 성공");
				} else {
					JOptionPane.showMessageDialog(this, "등록 실패");
				}

			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (pstmt != null) {
				try {
					pstmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static void main(String[] args) {
		new OracleApp();
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		insert();

	}
}

클래스를 하나 만들었습니다! 코드가 길어보이지만 사실 게임만들 때처럼 Frame디자인이 대부분입니다. 여기서 볼것은 이 클래스에 생성자에서 호출하고 있는 connect()메서드와 insert()메서드입니다.

//connect()메서드 쿼리 실행에 앞서, 오라클서버에 먼저 접속을 성공해야 합니다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
	public void connect() {
		// 오라클 드라이버를 클래스 로드!!
		try {// 로드하고 싶은 클래스의 경로..
			Class.forName("oracle.jdbc.driver.OracleDriver");
			System.out.println("드라이버 로드 성공");
			// 접속할 때 사용하는 JDBC객체는 DriverManager이다!!
			con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE", "batman", "1234");

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(this, "드라이버를 찾을 수 없습니다.");
		} catch (SQLException e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(this, "SQL에 접속할 수 없습니다.");
		}

	}

이것이 오라클서버에 접속할 수 있는 클래스입니다. 여기서 Class.forName(“oracle.jdbc.driver.OracleDriver”); 이 문장에서 forName()메서드

내부에 설치된 드라이버를 로드하여 그 드라이버가 java과 oracle db를 이어주는데 쓰는 도구가 됩니다. 그래서 미리 설치한 드라이버를 같은 프로젝트 폴더에 넣어주고 이제 이 문장을 실행하면 그 경로에 있는 드라이버를 불러옵니다! 그리고 DriveManager라는 녀석이 있는데 얘는 데이터 베이스에 접속할 때 사용하는 JDBC객체 중 하나입니다. 이 객체에 있는 getConnection()메서드를 사용하는데 이것은 드라이버를 사용하여 직접 db와 자바를 연결하는 것이죠. 이것은 오라클db기준으로 작성하였기 때문에 이부분을 유심히 볼 필요가 있습니다.

1
"jdbc:oracle:thin:@localhost:1521:XE", "batman", "1234"

첫번째 문자열은 접속하기 위한 url입니다. 오라클은 정해져있습니다. this방식의 도메인은 제 컴퓨터니까 localhost하고 포트번호는 오라클은 1521입니다. 그리고 XE버전이라 XE버전을 썼습니다. 그리고 뒤에는 user이름과 비밀번호를 적었습니다. 그리고 이것은 연결뿐만 아니라 Connection타입으로 반환까지 해줍니다. 따라서 멤버변수로 선언한 Connection타입의 객체 con에 담아주었습니다. 따라서 정상적으로 con에 값이 대입되었다면 정상적으로 접속이 된거라고 볼 수 있죠.

//insert()메서드입니다.

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
32
33
34
35
36
37
38
39
40
41
	public void insert() {
		// 이미 로그인을 성공한 상태이므로, 쿼리문을 수행할 수 있다
		// 쿼리문을 수행하기 위한 인터페이스가 바로 PreparedStatement 객체

		String name_value = book_name.getText();// 입력한 책의 이름
		int price_value = Integer.parseInt(price.getText());// String -->int

		String sql = "insert into book(book_id, book_name, price) values(seq_book.nextval, '" + name_value + "', "
				+ price_value + ")";

		PreparedStatement pstmt = null;// 멤버가 아닌 지역변수는 변수 선언시 초기화 되어있어야한다.

		try {
			if (con != null) {
				JOptionPane.showMessageDialog(this, "접속성공");

				// 이미 로그인을 성공한 상탵이므로, 쿼리문을 수행할 수 있다!!
				// 쿼리문을 수행하기 위한 인터페이스가 바로 PreparedStatement 객체
				pstmt = con.prepareStatement(sql);// 인스턴스를 만들기만 했음, 수행은 안했다..

				int result = pstmt.executeUpdate();// DML(insert,update,delete)수행메서드
				if (result > 0) {
					JOptionPane.showMessageDialog(this, "등록 성공");
				} else {
					JOptionPane.showMessageDialog(this, "등록 실패");
				}

			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (pstmt != null) {
				try {
					pstmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}

접속이 되었을 때 이제 이 메서드로 서버에 여기서 작성한 값이 명령어로 들어갑니다. 미리 만들어둔 두 텍스트필드에 각각 책 이름과 가격을 적으면 오라클에서 만들어 놓은 book테이블에 들어가게 하고 싶습니다. 그래서

1
2
		String name_value = book_name.getText();// 입력한 책의 이름
		int price_value = Integer.parseInt(price.getText());// String -->int

이렇게 입력한 값을 각각 name과 price 변수에 넣었습니다. price는 정수형이기 때문에 래퍼클래스로 형변환을 해주었습니다.

그리고 미리 sql이라는 문자열을 만들고 여기에 오라클db insert명령어 틀을 만들었습니다. 그리고 생소한 타입이 보입니다.

PreparedStatement 타입인데 이것은 쿼리문을 수행하기 위한 인터페이스입니다. 거기에 수행문을 넣어야겠죠? 그래서 connection에 있는 메서드를 활용하여 pstmt = con.prepareStatement(sql) 만들어 놓은 객체 pstmt에 저 수행문을 넣어줍니다. 그리고 이제 PreparedStatement에 있는 메서드 executeUpdate()메서드를 호출하여 수행문을 실행시킵니다.

Desktop View

executeUpdate() 메서드 리턴값을 보면 정상적으로 작동되면 row(행)수를 반환하고 에러나면 0을 반환

따라서 작동이 잘 되었다면 0보다 큰 값을 리턴합니다. 따라서 if문으로 잘 되었을 때와 안 됐을 때를 각각 나누어 안내문으로 뜨게 했습니다.

한번 실행해볼까요?

Desktop View

Desktop View

우왕.. java에서 db를 작동하니 너무 신기합니다!!

1
2
3
4
5
6
7
8
9
10
11
12
this.addWindowListener(new WindowAdapter() {
			// 사용자들이 윈도우 닫기 버튼을 누르면..
			public void windowClosing(WindowEvent e) {
				// db와 관련된 자원을 해제..
				try {
					con.close();
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
				System.exit(0);// 현재 프로그램 종료
			}
		});

이것은 프로그램 종료입니다. 보통 setDefaultCloseOperation(EXIT_ON_CLOSE); 문장을 쓰는데 이번엔 윈도우 리스너를 익명클래스로 만들어서 windowClosing()메서드를 사용했는데요 왜그럴까요? 왜냐면 단순히 프로그램만 끝내는것이 아니라 접속 해놓은것은 직접 해제시켜야하거든요.. 그래서 번거롭더라도 윈도우 리스너로 프로그램도 종료하고 db해제도 하기위함입니다.

이제 java로 db삽입을 했으니 이제 db에 있는 것을 java로 가져오겠습니다. 즉 java에서 select 명령어를 수행하는 것이죠!

//SelectApp

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.koreait.db1;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class SelectApp {

	// 1.드라이버 로드

	public static void main(String[] args) {
		Connection con = null;
		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");// 1.드라이버 로드
			System.out.println("드라이버 로드 성공");

			// 2.접속
			String url = "jdbc:oracle:thin:@localhost:1521:XE";
			String user = "batman";
			String pass = "1234";

			
			con = DriverManager.getConnection(url, user, pass);
			if (con != null) {
				System.out.println("접속 성공");
				String sql = "select * from book";
				PreparedStatement pstmt= con.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);//쿼리수행 객체의 인스턴스 얻기
			
				ResultSet rs= pstmt.executeQuery();//select문을 수행하는 메서드.. 그리고 원격지의 표가 자바의 메모리로 올라오며,
				//이 올라온 표는 자바의 ResultSet 객체로 받아둔다!
				
				rs.next();
				rs.next();//이 시점에 커서는 jsp레코드에 가있을 것임..
				
				System.out.println("현재 커서의 위치의 book_id는 "+rs.getInt("book_id"));
				System.out.println("현재 커서의 위치의 book_name은 "+rs.getString("book_name"));
				System.out.println("현재 커서의 위치의 pubdate은 "+rs.getString("pubdate"));
				
				//아래의 코드는 에러가 발생할 것이다.
				//이유? 개발자가 ResultSet을 생성할 때, 특정한 옵셜을 주지 않으면 디폴트는 전방향 전용 ResultSet이기 때문이다..
				//따라서 커서를 전,후 방향으로 마음대로 제어하려면 (Scroll이 가능한) 옵션을 부여해야 한다..
				rs.previous();//이전으로 한칸 후진!!
				System.out.println("현재 커서의 위치의 book_id는 "+rs.getInt("book_id"));
				System.out.println("현재 커서의 위치의 book_name은 "+rs.getString("book_name"));
				System.out.println("현재 커서의 위치의 pubdate은 "+rs.getString("pubdate"));
				
				rs.beforeFirst();//커서 원상복귀
				
				while(rs.next()==true) {//next()가 참일때까지....
					System.out.println("현재 커서의 위치의 book_id는 "+rs.getInt("book_id"));
				}
				
			} else {
				System.out.println("접속 실패");
			}

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			System.out.println("드라이버 로드 실패");
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if (con != null) {
					con.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

	}

}

오라클의 book 테이블에 존재하는 레코드를 자바의 콘솔에 출력해봅시다

(1)JDBC란? Java Database Connectivity의 약자로서, 자바로 데이터베이스를 연동하는 기술을 의미 jdbc기술은 java.sql 패키지에서 지원합니다.

(2)DB연동 순서 : 해당제품의 드라이버 로드 > 접속 > 쿼리문 실행 > db해제

이것도 아까 만든 클래스와 드라이버 로드와 접속까진 거의 비슷합니다. 다른점을 분석해보면서 어떻게 뭐가 다른지 알아보죠

1
PreparedStatement pstmt= con.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);

엄청나게 긴 문장이 있네요. 아까는 저기 con.prepareStatement(sql) 매개변수에 sql(실행문을 미리 문자열로 만든 것)넣었었는데 이번에는 2개를 더 추가했죠. ResultSet.TYPE_SCROLL_INSENSITIVE랑 ResultSet.CONCUR_READ_ONLY입니다. 전자는 java에서 ResultSet(테이블같은것)에서 커서를 위아래로 움직이면서 그 테이블의 레코드를 선택할 수 있도록 커서를 스크롤처럼 위아래로 움직일 수 있게 하였고, 후자는 자바에서 한 실행한 것이 실제 오라클 서버에 영향을 주지 않도록 읽기전용으로 설정하였습니다. 그리고 또 다른 점은

1
ResultSet rs= pstmt.executeQuery();

이것인데요, 아까 만든 클래스에서는 pstmt.executeUpdate()를 사용했고 그것을 int타입의 객체에 넣었는데 이번엔 메서드가 executeQuery()메서드를 사용했습니다. 이것은 select 문을 수행할 때 쓰는 메서드입니다. 그리고 반환형은 ResultSet은 데이터 베이스 결과집합을 나타내는 테이블입니다. 여기에는 커서가 있고 초기위치는 0번째 행입니다. 인덱스가 0이 아니라 진짜 0번째 행이라 한번 앞으로 가야 첫번째 행을 만납니다. 그리고 이 객체에는 nexe()라는 메서드가 있는데 이것은 커서를 앞으로 한칸 갑니다. 따라서 2번을 하면 2번째 행을 가리키겠죠?

1
2
3
System.out.println("현재 커서의 위치의 book_id는 "+rs.getInt("book_id"));
System.out.println("현재 커서의 위치의 book_name은 "+rs.getString("book_name"));
System.out.println("현재 커서의 위치의 pubdate은 "+rs.getString("pubdate"));

이 출력문으로 아까 오라클데이터베이스의 테이블에 2번재 행을 나타내겟습니다. 메서드는 getString()이나 getInt()를 사용하여 매개변수에 컬럼 이름을 문자열로 넣어서 2행에서 각각의값을 출력해보겠습니다.

Desktop View

잘 나옵니다!

그리고 커서를 이전으로 돌리는 것은 rs.previous();입니다. 그럼 현재 행에서 위로 한칸 가겠죠?rs.beforeFirst();이것은 커서를 원상복귀 시킵니다. 즉 0번째 칸으로 가겠네요.

1
2
3
	while(rs.next()==true) {//next()가 참일때까지....
					System.out.println("현재 커서의 위치의 book_id는 "+rs.getInt("book_id"));
				}

그리고 next()는 한칸 앞으로 가고 그 반환형은 boolean입니다. 그 앞에 레코드가 있으면 true를 없으면 false를 반환합니다. 그래서 while문과 잘 맞죠.. 이렇게하면 처음부터 끝까지 수행하겠죠?

Desktop View

잘 나오네요 ^^

이번시간은 여기까지 하고 다음에는 이제 표를 아예 구현해보겠습니다!

This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.