2020. 11. 5. 09:32ㆍ교육과정/KOSMO
키워드 : 비디오샵 완성본 / 싱글톤 패턴 / jfreechart
****
1. 비디오샵 - 대여관리 및 최종 완성
※ 대여관리 탭 구현 내용
: 대여 내역 JTable에 자동 출력
: 전화번호 입력 후 엔터 누르면 고객명 자동 출력
: 대여 패널 내 비디오번호까지 입력 후 대여 버튼 클릭시 DB 추가 및 JTable 자동 업데이트
: 반납 패널 내 비디오번호 입력 후 반납 버튼 클릭시 DB 추가 및 JTable 자동 업데이트
※ 비디오관리 탭 추가 구현 내용
: 비디오정보 입력 패널 내 텍스트 변경 후 수정 버튼 클릭시 DB 반영, 비디오검색 내역 자동 업데이트, 입력창 초기화
: 삭제 버튼 클릭시 선택된 비디오 정보 DB 삭제 반영, 비디오검색 내역 자동 업데이트, 입력창 초기화
※ 고객관리 탭 추가 구현 내용
: 고객정보 입력 창 상단으로 이동
: 검색 버튼 클릭시 고객정보 출력과 함께 검색창 초기화
◆ 오라클 DB 생성용 SQL ◆
-- 회원관리 테이블 생성
CREATE TABLE member (
tel varchar2(13),
name varchar2(20) NOT NULL,
sub_tel varchar2(13),
addr varchar2(100),
email varchar2(20),
CONSTRAINT pk_member_tel PRIMARY KEY (tel)
);
-- 비디오관리 테이블 생성
CREATE SEQUENCE video_num;
CREATE TABLE video (
vnum number(5),
title varchar2(40) NOT NULL,
genre varchar2(20) NOT NULL,
dir varchar2(30) NOT NULL,
act varchar2(20) NOT NULL,
sul varchar2(1000) NOT NULL,
cnt number(2),
CONSTRAINT pk_video_vnum PRIMARY KEY (vnum)
);
-- 대여관리 테이블 생성
CREATE SEQUENCE rent_num;
CREATE TABLE rent (
rnum number,
vnum number,
tel varchar2(13),
rent_date date,
return_date date,
CONSTRAINT pk_rent_rnum PRIMARY KEY (rnum),
CONSTRAINT fk_rent_tel FOREIGN KEY (tel) REFERENCES member(tel),
CONSTRAINT fk_rent_vnum FOREIGN KEY (vnum) REFERENCES video(vnum)
);
commit;
SELECT * FROM member;
SELECT * FROM video;
SELECT * FROM rent;
DESC member;
DESC video;
DESC rent;
◆ 비디오샵 메인 소스코드 ◆
<VideoShop 클래스>
package videoshop;
import java.awt.*;
import javax.swing.*;
import videoshop.view.CustomerView;
import videoshop.view.RentView;
import videoshop.view.VideoView;
public class VideoShop extends JFrame
{
CustomerView customer;
VideoView video;
RentView rent;
public VideoShop(){
customer = new CustomerView();
video = new VideoView();
rent = new RentView();
JTabbedPane pane = new JTabbedPane();
pane.addTab("고객관리", customer );
pane.addTab("비디오관리", video);
pane.addTab("대여관리", rent );
pane.setSelectedIndex(2);
getContentPane().add("Center", pane );
pack();
setVisible( true );
setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
}
public static void main(String[] args)
{
new VideoShop();
}
}
<DBcon 클래스>
package videoshop.model;
import java.sql.Connection;
import java.sql.DriverManager;
public class DBcon {
static Connection con;
String url = "jdbc:oracle:thin:@192.168.0.17:1521:orcl";
String user = "scott";
String pass = "tiger";
private DBcon() throws Exception {
// 1. 드라이버로딩
// 2. Connection 연결객체 얻어오기
Class.forName("oracle.jdbc.driver.OracleDriver");
con = DriverManager.getConnection(url,user, pass);
} // end of 생성자함수
public static Connection getInstance() throws Exception {
if (con == null) new DBcon();
return con;
}
}
◆ 고객관리 탭 소스코드 ◆
<CustomerVO 클래스>
package videoshop.model.vo;
public class CustomerVO {
String custTel;
String custName;
String custSubTel;
String custAddr;
String custmail;
// constructor
public CustomerVO() {
super();
}
// setter, getter
public String getCustTel() {
return custTel;
}
public void setCustTel(String custTel) {
this.custTel = custTel;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSubTel() {
return custSubTel;
}
public void setCustSubTel(String custSubTel) {
this.custSubTel = custSubTel;
}
public String getCustAddr() {
return custAddr;
}
public void setCustAddr(String custAddr) {
this.custAddr = custAddr;
}
public String getCustmail() {
return custmail;
}
public void setCustmail(String custmail) {
this.custmail = custmail;
}
}
<CustomerView 클래스>
package videoshop.view;
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import videoshop.model.CustomerDAO;
import videoshop.model.vo.CustomerVO;
public class CustomerView extends JPanel implements ActionListener
{
JFrame frm;
JTextField tfCustName, tfCustTel, tfCustTelAid, tfCustAddr, tfCustEmail;
JButton bCustRegist, bCustModify;
JTextField tfCustNameSearch, tfCustTelSearch;
JButton bCustNameSearch, bCustTelSearch;
// 모델단 - 비지니스 로직
CustomerDAO dao;
// 회원번호가 있을 경우에만
// int memberId;
/** constructor method **/
public CustomerView(){
addLayout();
eventProc();
try {
dao = new CustomerDAO();
System.out.println("DB 연결성공");
} catch (Exception e) {
System.out.println("DB 연결실패:" + e.toString());
e.printStackTrace();
}
} // end of 생성자함수
/** 버튼 이벤트와 객체 연결 **/
public void eventProc() {
bCustRegist.addActionListener(this);
bCustModify.addActionListener(this);
bCustNameSearch.addActionListener(this);
bCustTelSearch.addActionListener(this);
} // end of eventproc
/** 버튼 이벤트 핸들러 **/
public void actionPerformed(ActionEvent ev) {
Object evt = ev.getSource();
if (evt == bCustRegist) {
insertCustomer(); // 회원가입 버튼이 눌렸을 때
clearTextFields();
}else if (evt == bCustModify) {
modifyCustomer(); // 회원수정 버튼이 눌렸을 때
clearTextFields();
}else if (evt == bCustTelSearch) {
searchByTel(); // 전화번호로 검색 버튼이 눌렸을 때
tfCustTelSearch.setText(null); // 전화번호 검색창 초기화
}else if (evt == bCustNameSearch) {
searchByName(); // 이름으로 검색 버튼이 눌렸을 때
tfCustNameSearch.setText(null); // 이름 검색창 초기화
}
} // end of 버튼 이벤트 핸들러
/**
* 함수명 : clearTextFields()
* 인자 : 없음
* 리턴값 : 없음
* 역할 : 입력창 클리어
*/
void clearTextFields() {
tfCustName .setText(null);
tfCustTel .setText(null);
tfCustAddr .setText(null);
tfCustEmail .setText(null);
tfCustTelAid.setText(null);
tfCustNameSearch.setText(null);
tfCustTelSearch.setText(null);
} // end of clearTextFields
/**
* 함수명 : insertCustomer()
* 인자 : 없음
* 리턴값 : void
* 역할 : 회원가입을 위해 사용자 입력값을 받아서 모델로 전달
*/
void insertCustomer() {
// 1. 화면 입력값 얻어오기
// 2. VO 객체의 멤버 변수로 화면의 입려값들을 저장
// 3. 모델단의 regist() 호출
CustomerVO vo = new CustomerVO(); // 1.
vo.setCustName(tfCustName.getText()); // 2.
vo.setCustTel(tfCustTel.getText());
vo.setCustAddr(tfCustAddr.getText());
vo.setCustmail(tfCustEmail.getText());
vo.setCustSubTel(tfCustTelAid.getText());
try {
dao.regist(vo); // 3. 회원가입 메소드
}catch (Exception e) {
System.out.println("입력 실패: " + e.toString());
}
} // end of insertCustomer
/**
* 함수명 : modifyCustomer()
* 인자 : 없음
* 리턴값 : void
* 역할 : 수정버튼 눌렸을 때 Tel을 제외한 DB의 회원정보를 수정
*/
void modifyCustomer() {
CustomerVO vo = new CustomerVO();
vo.setCustTel (tfCustTel .getText());
vo.setCustName (tfCustName .getText());
vo.setCustAddr (tfCustAddr .getText());
vo.setCustSubTel(tfCustTelAid.getText());
vo.setCustmail (tfCustEmail .getText());
try {
dao.modify(vo);
}catch (Exception e) {
System.out.println("수정실패: " + e.toString());
}
} // end of modifyCustomer
/**
* 함수명 : searchByTel()
* 인자 : 없음
* 리턴값 : void
* 역할 : 전화번호로 검색시 고객 정보 불러와서 화면에 출력
*/
void searchByTel() {
String tel = tfCustTelSearch.getText();
try {
CustomerVO vo = dao.searchTel(tel);
// memberId = be.getCustNo();
tfCustName .setText(vo.getCustName());
tfCustTel .setText(vo.getCustTel());
tfCustTelAid.setText(vo.getCustSubTel());
tfCustAddr .setText(vo.getCustAddr());
tfCustEmail .setText(vo.getCustmail());
} catch (Exception e) {
System.out.println("전화번호 검색 실패: " + e.toString());
e.printStackTrace();
}
} // end of searchByTel
/**
* 함수명 : searchByName
* 인자 : 없음
* 리턴값 : void
* 역할 : 이름검색 버튼을 눌렀을 때 TextField의 값과 일치하는 회원정보를 출력
*/
void searchByName() {
String name = tfCustNameSearch.getText();
try {
ArrayList list = dao.searchName(name);
for (int i=0; i<list.size(); i++) {
CustomerVO vo = (CustomerVO)list.get(i);
tfCustName .setText(vo.getCustName());
tfCustTel .setText(vo.getCustTel());
tfCustTelAid.setText(vo.getCustSubTel());
tfCustAddr .setText(vo.getCustAddr());
tfCustEmail .setText(vo.getCustmail());
}
} catch (Exception e) {
System.out.println("검색실패:" + e.toString());
}
} // end of searchByName
/** 화면구성 **/
public void addLayout()
{
tfCustName = new JTextField(20);
tfCustTel = new JTextField(20);
tfCustTelAid = new JTextField(20);
tfCustAddr = new JTextField(20);
tfCustEmail = new JTextField(20);
tfCustNameSearch = new JTextField(20);
tfCustTelSearch = new JTextField(20);
bCustRegist = new JButton("회원가입");
bCustModify = new JButton("회원수정");
bCustNameSearch = new JButton("이름검색");
bCustTelSearch = new JButton("번호검색");
// 회원가입 부분 붙이기
// ( 그 복잡하다던 GridBagLayout을 사용해서 복잡해 보임..다른 쉬운것으로...대치 가능 )
JPanel pRegist = new JPanel();
pRegist.setLayout( new GridBagLayout() );
GridBagConstraints cbc = new GridBagConstraints();
cbc.weightx = 1.0;
cbc.weighty = 1.0;
cbc.fill = GridBagConstraints.BOTH;
cbc.gridx = 0; cbc.gridy = 0; cbc.gridwidth = 1; cbc.gridheight= 1;
pRegist.add( new JLabel(" 이 름 ") , cbc );
cbc.gridx = 1; cbc.gridy = 0; cbc.gridwidth = 1; cbc.gridheight= 1;
pRegist.add( tfCustName , cbc );
cbc.gridx = 2; cbc.gridy = 0; cbc.gridwidth = 1; cbc.gridheight= 1;
pRegist.add( bCustModify, cbc );
cbc.gridx = 3; cbc.gridy = 0; cbc.gridwidth = 1; cbc.gridheight= 1;
pRegist.add( bCustRegist, cbc );
cbc.gridx = 0; cbc.gridy = 1; cbc.gridwidth = 1; cbc.gridheight= 1;
pRegist.add( new JLabel(" 전 화 ") , cbc );
cbc.gridx = 1; cbc.gridy = 1; cbc.gridwidth = 1; cbc.gridheight= 1;
pRegist.add( tfCustTel , cbc );
cbc.gridx = 2; cbc.gridy = 1; cbc.gridwidth = 1; cbc.gridheight= 1;
pRegist.add( new JLabel(" 추가전화 ") , cbc );
cbc.gridx = 3; cbc.gridy = 1; cbc.gridwidth = 1; cbc.gridheight= 1;
pRegist.add( tfCustTelAid , cbc );
cbc.gridx = 0; cbc.gridy = 2; cbc.gridwidth = 1; cbc.gridheight= 1;
pRegist.add( new JLabel(" 주 소 ") , cbc );
cbc.gridx = 1; cbc.gridy = 2; cbc.gridwidth = 3; cbc.gridheight= 1;
pRegist.add( tfCustAddr , cbc );
cbc.gridx = 0; cbc.gridy = 3; cbc.gridwidth = 1; cbc.gridheight= 1;
pRegist.add( new JLabel(" 이메일 ") , cbc );
cbc.gridx = 1; cbc.gridy = 3; cbc.gridwidth = 3; cbc.gridheight= 1;
pRegist.add( tfCustEmail , cbc );
// 회원검색 부분 붙이기
JPanel pSearch = new JPanel();
pSearch.setLayout( new GridLayout(2, 1) );
JPanel pSearchName = new JPanel();
pSearchName.add( new JLabel(" 이 름 "));
pSearchName.add( tfCustNameSearch );
pSearchName.add( bCustNameSearch );
JPanel pSearchTel = new JPanel();
pSearchTel.add( new JLabel(" 전화번호 "));
pSearchTel.add( tfCustTelSearch );
pSearchTel.add( bCustTelSearch );
pSearch.add( pSearchName );
pSearch.add( pSearchTel );
// 전체 패널에 붙이기
setLayout( new BorderLayout() );
// add("Center", pRegist );
add("North", pRegist );
add("South", pSearch );
} // end of addLayout
} // end of CustomerView 클래스
<CustomerDAO 클래스>
package videoshop.model;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import javax.swing.JOptionPane;
import videoshop.model.vo.CustomerVO;
public class CustomerDAO {
Connection con;
/** constructor method **/
public CustomerDAO() throws Exception{
con = DBcon.getInstance();
} // end of 생성자함수
/** 회원가입 **/
public void regist( CustomerVO cust ) throws Exception {
/*
1. sql 작성하기 ( insert 문 작성 : CustomerVO의 멤버변수로 각각 지정)
2. sql 전송객체 얻어오기( PreparedStatement가 더 적합할 듯 )
3. sql 전송 ( 전송전에 ?로 지정 )
4. 닫기 ( Connection은 닫으면 안됨 )
*/
PreparedStatement ps=null; // 2-1.
try {
String sql = "INSERT INTO member " // 1.
+ "(tel, name, sub_tel, addr, email) "
+ "VALUES (?, ?, ?, ?, ?)";
ps = con.prepareStatement(sql); // 2-2.
ps.setString(1, cust.getCustTel());
ps.setString(2, cust.getCustName());
ps.setString(3, cust.getCustSubTel());
ps.setString(4, cust.getCustAddr());
ps.setString(5, cust.getCustmail());
int result = ps.executeUpdate(); // 3.
System.out.println(result+"행을 입력");
}finally {
try { ps.close(); } catch(Exception e) {} // 4
}
} // end of regist
/** 회원정보 수정 **/
public void modify( CustomerVO cust) throws Exception{
/*
1. sql 작성하기 ( update 문 작성 : CustomerVO의 멤버변수로 각각 지정)
2. sql 전송객체 얻어오기( PreparedStatement가 더 적합할 듯 )
3. sql 전송 ( 전송전에 ?로 지정 )
4. 닫기 ( Connection은 닫으면 안됨 )
*/
PreparedStatement ps=null; // 2-1.
try {
String sql = "UPDATE member SET " // 1.
+ "name=?, sub_tel=?, addr=?, email=? "
+ "WHERE tel=?";
ps = con.prepareStatement(sql); // 2-2.
ps.setString(1, cust.getCustName());
ps.setString(2, cust.getCustSubTel());
ps.setString(3, cust.getCustAddr());
ps.setString(4, cust.getCustmail());
ps.setString(5, cust.getCustTel());
int result = ps.executeUpdate(); // 3.
System.out.println(result+"행을 입력");
}finally {
try { ps.close(); } catch(Exception e) {} // 4.
}
} // end of modify
/** 이름 검색 **/
public ArrayList searchName( String name ) throws Exception {
/*
1. sql 작성하기 ( select 문
: 함수의 인자로 넘어온 이름과 같은 조건의 레코드 검색 )
2. sql 전송객체 얻어오기( Statement / PreparedStatement 모두 적합 )
3. sql 전송 ( ResultSet 의 데이타를 얻어서 멤버 필드에 지정 )
4. 닫기 ( Connection은 닫으면 안됨 )
# 생각해 보기
- 동명 이인이 여러명 인 경우는 어떻게 처리할까?
*/
ArrayList list = new ArrayList();
PreparedStatement ps = null; // 2-1.
try {
String sql = // 1.
"SELECT * FROM member WHERE name = ?";
ps = con.prepareStatement(sql); // 2-2.
ps.setString(1, name);
ResultSet rs = ps.executeQuery(); // 3.
System.out.println(rs);
if (rs.next()) {
CustomerVO vo = new CustomerVO();
vo.setCustName (rs.getString("NAME"));
vo.setCustTel (rs.getString("TEL"));
vo.setCustSubTel(rs.getString("SUB_TEL"));
vo.setCustAddr (rs.getString("ADDR"));
vo.setCustmail (rs.getString("EMAIL"));
list.add(vo);
}
} finally {
try { ps.close(); } catch(Exception e) {} // 4.
}
return list;
} // end of searchName
/** 전화번호 검색 **/
public CustomerVO searchTel( String tel ) throws Exception {
/*
1. sql 작성하기 ( select 문
: 함수의 인자로 넘어온 전화번호과 같은 조건의 레코드 검색 )
2. sql 전송객체 얻어오기( Statement / PreparedStatement 모두 적합 )
3. sql 전송 ( ResultSet 의 데이타를 얻어서 멤버 필드에 지정 )
결과집합의 컬럼값을 CustomerVO의 각각 멤버 지정
4. 닫기 ( Connection은 닫으면 안됨 )
*/
CustomerVO vo = new CustomerVO();
PreparedStatement ps = null; // 2-1.
try {
String sql = // 1.
"SELECT * FROM member WHERE tel = ?";
ps = con.prepareStatement(sql); // 2-2.
ps.setString(1, tel);
ResultSet rs = ps.executeQuery(); // 3.
if (rs.next()) {
vo.setCustName(rs.getString("NAME"));
vo.setCustTel(rs.getString("TEL"));
vo.setCustSubTel(rs.getString("SUB_TEL"));
vo.setCustAddr(rs.getString("ADDR"));
vo.setCustmail(rs.getString("EMAIL"));
}
}finally {
try { ps.close(); } catch(Exception e) {} // 4.
}
return vo;
} // end of searchTel
} // end of CustomerDAO
◆ 비디오관리 탭 소스코드 ◆
<VideoVO 클래스>
package videoshop.model.vo;
public class VideoVO {
// 멤버변수 - Video 테이블의 컬럼과 매칭하도록 선언
int vnum;
String title;
String genre;
String dir;
String act;
String sul;
int count;
// 생성자 함수
public VideoVO() {
}
// setter, getter
public int getVnum() {
return vnum;
}
public void setVnum(int vnum) {
this.vnum = vnum;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getGenre() {
return genre;
}
public void setGenre(String genre) {
this.genre = genre;
}
public String getDir() {
return dir;
}
public void setDir(String dir) {
this.dir = dir;
}
public String getAct() {
return act;
}
public void setAct(String act) {
this.act = act;
}
public String getSul() {
return sul;
}
public void setSul(String sul) {
this.sul = sul;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
<VideoView 클래스>
package videoshop.view;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import javax.swing.table.AbstractTableModel;
import videoshop.model.VideoDAO;
import videoshop.model.vo.VideoVO;
public class VideoView extends JPanel
{
// member field
JTextField tfVideoNum, tfVideoTitle, tfVideoDirector, tfVideoActor;
JComboBox comVideoGenre;
JTextArea taVideoContent;
JCheckBox cbMultiInsert;
JTextField tfInsertCount;
JButton bVideoInsert, bVideoModify, bVideoDelete;
JComboBox comVideoSearch;
JTextField tfVideoSearch;
JTable tableVideo;
VideoTableModel tbModelVideo;
VideoDAO dao;
//##############################################
// constructor method
public VideoView(){
addLayout(); // 화면설계
initStyle();
eventProc();
connectDB(); // DB연결
} // end of 생성자함수
/** DB연결 **/
public void connectDB() {
try {
dao = new VideoDAO();
System.out.println("DB 연결성공 - 비디오관리");
} catch (Exception e) {
System.out.println("DB 연결실패 - 비디오관리");
}
} // end of connectDB
/** 버튼 이벤트와 객체 연결 **/
public void eventProc(){
ButtonEventHandler hdlr = new ButtonEventHandler();
bVideoInsert.addActionListener(hdlr);
bVideoModify.addActionListener(hdlr);
bVideoDelete.addActionListener(hdlr);
tfVideoSearch.addActionListener(hdlr);
/** 다중입고 체크박스 이벤트 **/
cbMultiInsert.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
boolean flag = cbMultiInsert.isSelected();
tfInsertCount.setEditable(flag);
}
}); // end of 다중입고 체크박스 이벤트
/** 검색된 데이터 클릭했을 때 좌측에 비디오정보 출력하기 **/
tableVideo.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int col = 0; // 비디오번호는 0번째 컬럼이라 고정
int row = tableVideo.getSelectedRow();
int vNum = (Integer)tableVideo.getValueAt(row, col);
try {
VideoVO vo = dao.searchByPK(vNum);
tfVideoTitle.setText(vo.getTitle());
tfVideoDirector.setText(vo.getDir());
tfVideoActor.setText(vo.getAct());
taVideoContent.setText(vo.getSul());
comVideoGenre.setSelectedItem(vo.getGenre());
tfVideoNum.setText(Integer.toString(vo.getVnum()));
} catch (Exception e1) {
System.out.println("검색 실패:" + e.toString());
e1.printStackTrace();
}
}
}); // end of 비디오정보 출력하기
} // end of eventproc
/** 버튼 이벤트 핸들러를 만들기 위한 이너클래스 **/
class ButtonEventHandler implements ActionListener{
public void actionPerformed(ActionEvent ev){
Object o = ev.getSource();
if(o==bVideoInsert){
registVideo(); // 비디오 등록
clearTextField();
}
else if(o==bVideoModify){
modifyVideo(); // 비디오 정보 수정
clearTextField();
searchVideo();
}
else if(o==bVideoDelete){
deleteVideo(); // 비디오 정보 삭제
clearTextField();
searchVideo();
}
else if(o==tfVideoSearch){
searchVideo(); // 비디오 검색
}
}
} // end of 버튼 이벤트 핸들러
/** 입력창 초기화 **/
public void clearTextField() {
tfVideoNum .setText(null);
tfVideoTitle .setText(null);
tfVideoActor .setText(null);
tfVideoDirector .setText(null);
taVideoContent.setText(null);
tfInsertCount.setEditable(true);
tfInsertCount.setText("1");
tfInsertCount.setEditable(false);
} // end of clearTextField
/** 입고 클릭시 - 비디오 정보 등록 **/
public void registVideo(){
// 1. 화면에서 사용자 입력값 얻어오기
// 2. VedioVO의 멤버변수로 1번값들을 지정
// 3. 모델단(VedioDAO)의 insertVideo() 호출
String genre = (String)comVideoGenre.getSelectedItem();
String title = tfVideoTitle.getText();
String actor = tfVideoActor.getText();
String director = tfVideoDirector.getText();
String content = taVideoContent.getText();
int count = Integer.parseInt(tfInsertCount.getText());
VideoVO vo = new VideoVO();
vo.setGenre(genre);
vo.setTitle(title);
vo.setAct(actor);
vo.setDir(director);
vo.setSul(content);
vo.setCount(count);
try {
dao.insertVideo(vo, count);
} catch (Exception e) {
System.out.println("입고실패:" + e.toString());
e.printStackTrace();
}
} // end of registVideo
/** 비디오번호, 다중입고 수량 입력 불가 처리 **/
public void initStyle(){
tfVideoNum.setEditable(false);
tfInsertCount.setEditable(false);
tfInsertCount.setHorizontalAlignment(JTextField.RIGHT);
} // end of iniStyle
/** 수정 클릭시 - 비디오 정보 수정 **/
public void modifyVideo(){
VideoVO vo = new VideoVO();
vo.setGenre((String)comVideoGenre.getSelectedItem());
vo.setTitle(tfVideoTitle.getText());
vo.setDir(tfVideoDirector.getText());
vo.setAct(tfVideoActor.getText());
vo.setSul(taVideoContent.getText());
vo.setVnum(Integer.parseInt(tfVideoNum.getText()));
try {
dao.modify(vo);
}catch (Exception e) {
System.out.println("수정실패:" + e.toString());
}
} // end of modifyVideo
/** 삭제 클릭시 - 비디오 정보 삭제 **/
public void deleteVideo(){
String vnum = tfVideoNum.getText();
try {
dao.delete(vnum);
} catch (Exception e) {
System.out.println("삭제실패:" + e.toString());
}
} // end of deleteVideo
/** 비디오현황검색 **/
public void searchVideo(){
int idx = comVideoSearch.getSelectedIndex();
String word = tfVideoSearch.getText();
try {
ArrayList data = dao.searchVideo(idx, word);
tbModelVideo.data = data;
tableVideo.setModel(tbModelVideo);
tbModelVideo.fireTableDataChanged();
} catch (Exception e) {
System.out.println("검색실패:" + e.toString());
e.printStackTrace();
}
} // end of searchVideo
/** 화면설계 메소드 **/
public void addLayout(){
//멤버변수의 객체 생성
tfVideoNum = new JTextField();
tfVideoTitle = new JTextField();
tfVideoDirector = new JTextField();
tfVideoActor = new JTextField();
String []cbJanreStr = {"멜로","엑션","스릴","코미디"};
comVideoGenre = new JComboBox(cbJanreStr);
taVideoContent = new JTextArea();
cbMultiInsert = new JCheckBox("다중입고");
tfInsertCount = new JTextField("1",5);
bVideoInsert = new JButton("입고");
bVideoModify = new JButton("수정");
bVideoDelete = new JButton("삭제");
String []cbVideoSearch = {"제목","감독"};
comVideoSearch = new JComboBox(cbVideoSearch);
tfVideoSearch = new JTextField(15);
tbModelVideo = new VideoTableModel();
tableVideo = new JTable(tbModelVideo);
//*********************************************************************
// 화면 구성
setLayout(new GridLayout(1,2));
// 왼쪽 영역
JPanel pLeft = new JPanel();
pLeft.setBorder(new TitledBorder("비디오정보 입력"));
pLeft.setLayout(new BorderLayout());
// 왼쪽 중앙 영역
JPanel pLeftCenter = new JPanel();
pLeftCenter.setLayout(new BorderLayout());
JPanel pLeftCenterNorth = new JPanel();
pLeftCenterNorth.setLayout(new GridLayout(5,2));
pLeftCenterNorth.add(new JLabel("비디오번호"));
pLeftCenterNorth.add(tfVideoNum);
pLeftCenterNorth.add(new JLabel("장르"));
pLeftCenterNorth.add(comVideoGenre);
pLeftCenterNorth.add(new JLabel("제목"));
pLeftCenterNorth.add(tfVideoTitle);
pLeftCenterNorth.add(new JLabel("감독"));
pLeftCenterNorth.add(tfVideoDirector);
pLeftCenterNorth.add(new JLabel("배우"));
pLeftCenterNorth.add(tfVideoActor);
JPanel pLeftCenterCenter = new JPanel();
pLeftCenterCenter.setLayout(new BorderLayout());
pLeftCenterCenter.add(new JLabel("설명"),BorderLayout.WEST);
pLeftCenterCenter.add(taVideoContent, BorderLayout.CENTER);
pLeftCenter.add(pLeftCenterNorth, BorderLayout.NORTH);
pLeftCenter.add(pLeftCenterCenter, BorderLayout.CENTER);
// 왼쪽 아래 영역
JPanel pLeftSouth = new JPanel();
pLeftSouth.setLayout(new GridLayout(2,1));
JPanel pLeftSouth1 = new JPanel();
pLeftSouth1.setBorder(new TitledBorder("다중입력시 선택하세요"));
pLeftSouth1.add(cbMultiInsert);
pLeftSouth1.add(tfInsertCount);
pLeftSouth1.add(new JLabel("개"));
JPanel pLeftSouth2 = new JPanel();
pLeftSouth2.setLayout(new GridLayout(1,3));
pLeftSouth2.add(bVideoInsert);
pLeftSouth2.add(bVideoModify);
pLeftSouth2.add(bVideoDelete);
pLeftSouth.add(pLeftSouth1);
pLeftSouth.add(pLeftSouth2);
pLeft.add(pLeftCenter, BorderLayout.CENTER);
pLeft.add(pLeftSouth, BorderLayout.SOUTH);
// 오른쪽 영역
JPanel pRight = new JPanel();
pRight.setBorder(new TitledBorder("비디오검색"));
pRight.setLayout(new BorderLayout());
JPanel pRightNorth = new JPanel();
pRightNorth.add(comVideoSearch);
pRightNorth.add(tfVideoSearch);
pRight.add(pRightNorth, BorderLayout.NORTH);
pRight.add(new JScrollPane(tableVideo), BorderLayout.CENTER);
add(pLeft);
add(pRight);
} // end of addLayout
/** JTable 관리를 위한 TableModel 이너클래스 **/
class VideoTableModel extends AbstractTableModel {
ArrayList data = new ArrayList();
String [] columnNames = {"비디오번호","제목","감독","배우"};
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return data.size();
}
public Object getValueAt(int row, int col) {
ArrayList temp = (ArrayList)data.get( row );
return temp.get( col );
}
public String getColumnName(int col){
return columnNames[col];
}
} // end of VideoTableModel
} // end of VideoView 클래스
<VideoDAO 클래스>
package videoshop.model;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import videoshop.model.vo.VideoVO;
public class VideoDAO{
Connection con;
/** constructor method **/
public VideoDAO() throws Exception{
con = DBcon.getInstance();
} // end of 생성자 함수
/** 입고 : VideoView의 registVideo()에서 작동할 메소드 **/
public void insertVideo(VideoVO vo, int count) throws Exception {
PreparedStatement ps = null;
try {
// 3. sql 문장 만들기
String sql = "INSERT INTO video "
+ "(vnum, title, genre, dir, act, sul, cnt) "
+ "VALUES (video_num.NEXTVAL, ?,?,?,?,?,?)";
// 4. sql 전송객체 (PreparedStatement)
ps = con.prepareStatement(sql);
ps.setString(1, vo.getTitle());
ps.setString(2, vo.getGenre());
ps.setString(3, vo.getDir());
ps.setString(4, vo.getAct());
ps.setString(5, vo.getSul());
ps.setInt (6, vo.getCount());
// 5. sql 전송
int result = 0;;
for (int i=0; i<count; i++) {
result = ps.executeUpdate();
}
System.out.println(result+"행을 입력");
} finally {
// 6. 닫기 ( 연결객체 닫지 않음 )
try { ps.close(); } catch(Exception e) {}
}
} // end of insertVideo
/** 수정 : VideoView의 modifyVideo()에서 작동할 메소드 **/
public void modify(VideoVO vo) throws Exception {
PreparedStatement ps = null;
try {
String sql = "UPDATE video SET genre=?, title=?, dir=?, "
+ "act=?, sul=? WHERE vnum=?";
ps = con.prepareStatement(sql);
ps.setString(1, vo.getGenre());
ps.setString(2, vo.getTitle());
ps.setString(3, vo.getDir());
ps.setString(4, vo.getAct());
ps.setString(5, vo.getSul());
ps.setInt(6, vo.getVnum());
System.out.println(sql);
int result = ps.executeUpdate();
System.out.println(result+"행을 입력");
} finally {
try { ps.close(); } catch(Exception e) {}
}
} // end of modify
/** 삭제 : VideoView의 deleteVideo()에서 작동할 메소드 **/
public void delete(String vnum) throws Exception {
Statement st = null;
try {
String sql = "DELETE FROM video WHERE vnum=" + vnum + "";
st = con.createStatement();
System.out.println(sql);
int result = st.executeUpdate(sql);
System.out.println(result+"행을 입력");
} finally {
try { st.close(); } catch(Exception e) {}
}
} // end of delete
/** 비디오검색 : VideoView의 searchVideo()에서 작동할 메소드 **/
public ArrayList searchVideo(int idx, String word) throws Exception {
String[] colName = {"title", "dir"};
String sql = "SELECT vnum, title, dir, act FROM video WHERE "
+ colName[idx] + " LIKE '%" + word + "%'";
// sql문 내에 자바의 변수를 사용할 경우에는 자동 '' 문제로 ps를 사용할 수 없다.
System.out.println(sql);
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql);
ArrayList data = new ArrayList();
while (rs.next()) {
ArrayList temp = new ArrayList();
temp.add(rs.getInt("vnum"));
temp.add(rs.getString("title"));
temp.add(rs.getString("dir"));
temp.add(rs.getString("act"));
data.add(temp);
}
rs.close();
st.close();
return data;
} // end of searchVideo
/** 검색된 데이터를 클릭했을 때 좌측에 비디오정보 출력하기**/
public VideoVO searchByPK(int vNum) throws Exception {
VideoVO vo = new VideoVO();
PreparedStatement ps = null;
try {
String sql = "SELECT * FROM video WHERE vnum = ?";
ps = con.prepareStatement(sql);
ps.setInt(1, vNum);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
vo.setTitle (rs.getString("TITLE"));
vo.setDir (rs.getString("DIR"));
vo.setAct(rs.getString("ACT"));
vo.setSul(rs.getString("SUL"));
vo.setGenre(rs.getString("GENRE"));
vo.setVnum(rs.getInt("VNUM"));
}
} finally {
try { ps.close(); } catch(Exception e) {}
}
return vo;
} // end of searchByPK
} // end of VideoDAO 클래스
◆ 대여관리 탭 소스코드 ◆
<RentVO 클래스>
package videoshop.model.vo;
public class RentVO {
// 멤버변수 - Video 테이블의 컬럼과 매칭하도록 선언
int rnum;
int vnum;
String tel;
String rent_data;
String return_date;
public RentVO () {
}
public int getRnum() {
return rnum;
}
public void setRnum(int rnum) {
this.rnum = rnum;
}
public int getVnum() {
return vnum;
}
public void setVnum(int vnum) {
this.vnum = vnum;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
public String getRent_data() {
return rent_data;
}
public void setRent_data(String rent_data) {
this.rent_data = rent_data;
}
public String getReturn_date() {
return return_date;
}
public void setReturn_date(String return_date) {
this.return_date = return_date;
}
}
<RentView 클래스>
package videoshop.view;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import javax.swing.table.AbstractTableModel;
import videoshop.model.RentDAO;
import videoshop.model.vo.CustomerVO;
import videoshop.model.vo.RentVO;
public class RentView extends JPanel
{
// 멤버 변수
JTextField tfRentTel, tfRentCustName, tfRentVideoNum, tfReturnVideoNum;
JButton bRent, bReturn;
JTable tableRent;
RentTableModel tableRecentList;
RentDAO dao;
/** constructor method **/
public RentView(){
addLayout();
eventProc();
connectDB();
select();
} // end of 생성자함수
/** DB연결 **/
public void connectDB() {
try {
dao = new RentDAO();
System.out.println("DB 연결성공 - 대여관리");
} catch (Exception e) {
System.out.println("DB 연결실패 - 대여관리:" + e.toString());
}
} // end of connectDB
/** 버튼 이벤트와 객체 연결 **/
public void eventProc() {
ButtonEventHandler hdlr = new ButtonEventHandler();
bRent.addActionListener(hdlr);
bReturn.addActionListener(hdlr);
tfRentTel.addActionListener(hdlr);
} // end of eventProc
/** 버튼 이벤트 핸들러를 만들기 위한 이너클래스 **/
class ButtonEventHandler implements ActionListener {
public void actionPerformed(ActionEvent ev) {
Object o = ev.getSource();
if (o==bRent) {
rentVideo();
clearTextField();
select();
} else if (o==bReturn) {
returnVideo();
clearTextField();
select();
} else if (o==tfRentTel) {
searchByTel();
}
}
}// end of 버튼 이벤트 핸들러
/** 입력창 초기화 **/
public void clearTextField() {
tfRentTel.setText(null);
tfRentCustName.setText(null);
tfRentVideoNum.setText(null);
tfReturnVideoNum.setText(null);
} // end of clearTextField
/**
* 함수명 : rentVideo
* 인자 : 없음
* 리턴형 : void
* 역할 : 화면에 입력한 고객의 전화번호와 비디오번호를 받아서 대여 관리 테이블에 추가하는 역할
*/
public void rentVideo() {
String tel = tfRentTel.getText();
String vNum = tfRentVideoNum.getText();
try {
dao.rent(tel, vNum);
} catch (Exception e) {
System.out.println("대여실패: " + e.toString());
e.printStackTrace();
}
} // end of rentVideo
/**
* 함수명 : returnVideo
* 인자 : 없음
* 리턴형 : void
* 역할 : 화면에 입력한 비디오번호를 받아서 대여 관리 테이블에서 반납일을 오늘날짜로 입력
*/
public void returnVideo() {
String vNum = tfReturnVideoNum.getText();
try {
dao.returnV(vNum);
} catch (Exception e) {
System.out.println("반납실패: " + e.toString());
e.printStackTrace();
}
} // end of returnVideo
/** 전화번호 텍스트 입력 후 엔터시 호출 될 메소드 **/
void searchByTel() {
String tel = tfRentTel.getText();
try {
CustomerVO vo = dao.searchTel(tel);
tfRentCustName.setText(vo.getCustName());
} catch (Exception e) {
System.out.println("전화번호 검색 실패: " + e.toString());
e.printStackTrace();
}
} // end of searchByTel
/** 미납목록을 화면에 출력하는 메소드 **/
void select() {
try {
ArrayList data = dao.select();
tableRecentList.data = data;
tableRent.setModel(tableRecentList);
tableRecentList.fireTableDataChanged();
} catch (Exception e) {
System.out.println("미납목록 검색 실패");
e.printStackTrace();
}
} // end of select
// 화면 구성
public void addLayout() {
tfRentTel = new JTextField(20);
tfRentCustName = new JTextField(20);
tfRentVideoNum = new JTextField(20);
tfReturnVideoNum = new JTextField(20);
bRent = new JButton("대여");
bReturn = new JButton("반납");
tableRecentList = new RentTableModel();
tableRent = new JTable(tableRecentList);
//**********************************************//
setLayout(new BorderLayout());
JPanel pNorth = new JPanel();
pNorth.setLayout(new GridLayout(1,2));
JPanel pNorthLeft = new JPanel();
pNorthLeft.setBorder(new TitledBorder("대여"));
pNorthLeft.setLayout(new GridLayout(4,2));
pNorthLeft.add(new JLabel("전 화 번 호"));
pNorthLeft.add(tfRentTel);
pNorthLeft.add(new JLabel("고 객 명"));
pNorthLeft.add(tfRentCustName);
pNorthLeft.add(new JLabel("비디오 번호"));
pNorthLeft.add(tfRentVideoNum);
pNorthLeft.add(bRent);
JPanel pNorthRight= new JPanel();
pNorthRight.setBorder(new TitledBorder("반납"));
pNorthRight.add(new JLabel("비디오 번호"));
pNorthRight.add(tfReturnVideoNum);
pNorthRight.add(bReturn);
pNorth.add(pNorthLeft);
pNorth.add(pNorthRight);
JPanel pCenter = new JPanel();
pCenter.setLayout(new BorderLayout());
pCenter.add(new JScrollPane(tableRent), BorderLayout.CENTER);
add(pNorth, BorderLayout.NORTH);
add(pCenter, BorderLayout.CENTER);
} // end of addLayout
/** JTable 관리를 위한 TableModel 이너클래스 **/
class RentTableModel extends AbstractTableModel {
ArrayList data = new ArrayList();
String [] columnNames
= {"비디오번호", "비디오제목", "고 객 명", "전화번호", "반납예정일", "반납여부"};
public int getRowCount() {
return data.size();
}
public int getColumnCount() {
return columnNames.length;
}
public Object getValueAt(int row, int col) {
ArrayList temp = (ArrayList)data.get(row);
return temp.get(col);
}
public String getColumnName(int col) {
return columnNames[col];
}
} // end of RentTableModel
} // end of RentView 클래스
<RentDAO 클래스>
package videoshop.model;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import videoshop.model.vo.CustomerVO;
public class RentDAO {
Connection con;
/** constructor method **/
public RentDAO () throws Exception {
con = DBcon.getInstance();
} // end of 생성자 함수
/**
* 함수명 : rent
* 인자 : String tel, String vNum
* 리턴형 : void
* 역할 : 전화번호와 비디오번호를 바탕으로 rent 테이블에 SQL로 입력하는 역할
*/
public void rent(String tel, String vNum) throws Exception {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO rent (RNUM, TEL, VNUM, RENT_DATE) "
+ "VALUES (rent_num.nextval, ?, ?, sysdate)";
ps = con.prepareStatement(sql);
ps.setString(1, tel);
ps.setString(2, vNum);
int result = ps.executeUpdate();
System.out.println(result+"행을 입력");
} finally {
try { ps.close(); } catch(Exception e) {}
}
} // end of rent
/**
* 함수명 : returnV
* 인자 : String vNum
* 리턴형 : void
* 역할 : 비디오번호를 바탕으로 반납날짜를 입력하는 SQL을 전송하는 역할
*/
public void returnV(String vNum) throws Exception {
PreparedStatement ps = null;
try {
String sql = "UPDATE rent SET return_date=sysdate "
+ "WHERE vnum=? AND return_date is null";
System.out.println(sql);
ps = con.prepareStatement(sql);
ps.setString(1, vNum);
int result = ps.executeUpdate();
System.out.println(result+"행을 입력");
} finally {
try { ps.close(); } catch(Exception e) {}
}
} // end of returnV
/**
* 함수명 : searchTel
* 인자 : String tel
* 리턴형 : CustomerVO vo
* 역할 : 전화번호를 바탕으로 SQL에서 이름을 가져오는 역할
*/
public CustomerVO searchTel (String tel) throws Exception {
CustomerVO vo = new CustomerVO();
PreparedStatement ps = null;
try {
String sql = "SELECT * FROM member WHERE tel = ?";
ps = con.prepareStatement(sql);
ps.setString(1, tel);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
vo.setCustName(rs.getString("NAME"));
}
} finally {
try { ps.close(); } catch(Exception e) {}
}
return vo;
} // end of searchTel
/**
* 함수명 : select
* 인자 :
* 리턴형 :
* 역할 : 화면에 미납목록이 출력되도록 SQL에서 데이터를 가져오는 역할
*/
public ArrayList select() throws Exception {
String sql = "SELECT r.vnum vnum, v.title title, m.name name, "
+ "r.tel tel, r.rent_date+3 rent_date, "
+ "r.return_date return_date "
+ "FROM rent r INNER JOIN member m ON r.tel=m.tel "
+ "INNER JOIN video v ON r.vnum=v.vnum "
+ "WHERE r.return_date is null";
// sql문 내에 자바의 변수를 사용할 경우에는 자동 '' 문제로 ps를 사용할 수 없다.
System.out.println(sql);
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql);
ArrayList data = new ArrayList();
while (rs.next()) {
ArrayList temp = new ArrayList();
temp.add(rs.getInt("vnum"));
temp.add(rs.getString("title"));
temp.add(rs.getString("name"));
temp.add(rs.getString("tel"));
temp.add(rs.getString("rent_date"));
temp.add(rs.getString("return_date"));
data.add(temp);
}
rs.close();
st.close();
return data;
} // end of select
} // end of RentDAO 클래스
2. 싱글톤 패턴 (Singleton Pattern)
: 고정된 메모리 영역을 얻으면서 한 번의 new로 인스턴스를 사용하기 떄문에 메모리 낭비를 방지할 수 있다. 또한 싱글톤으로 만들어진 클래스의 인스턴스는 전역 인스턴스이기 때문에 다른 클래스의 인스턴스들이 데이터를 공유하기 쉽다.
: Database Connection 처럼 공통된 객체를 여러개 생성해서 사용해야 하는 상황에서 많이 사용한다.
ex) 스레드풀, 캐시, 대화상자, 사용자 설정, 레지스트리 설정, 로그 기록 객체, 안드로이드 앱 등
: 인스턴스가 절대적으로 한 개만 존재하는 것을 보증하고 싶을 경우에 사용한다.
: 두 번째 이용시부터는 객체 로딩 시간이 현저하게 줄어 성능이 좋아지는 장점이 있다.
: 단점! : 싱글톤 인스턴스가 너무 많은 일을 하거나 많은 데이터를 공유시킬 경우, 다른 클래스의 인스턴스들 간에 결합도가 높아져 객체 지향 설계 원칙에 어긋날 수 있다. 또한 수정작업과 테스트에 어려움을 겪을 수 있다. 멀티스레드 환경에서 동기화 처리를 하지 않을 경우, 인스턴스가 두 개 생성되는 등의 상황이 발생할 수 있다.
◆ 비디오샵에서의 싱글톤 사용 ◆
(1) 3개의 DAO마다 각각 Connection con을 new로 생성하는 것은 비효율적이다.
-> 단 한 번만 con을 생성하여 DAO들이 공유하고자 함
-> 공유 = static 처리 필요함
(2) Connection con이 단 한 번 생성될 클래스 DBcon을 만든다.
(3) DBcon의 생성자 함수에서 드라이버를 로딩한다. (예외던지기 처리도 해줌)
public class DBcon {
static Connection con;
String url = "jdbc:oracle:thin:@192.168.0.17:1521:orcl";
String user = "scott";
String pass = "tiger";
private DBcon() throws Exception {
Class.forName("oracle.jdbc.driver.OracleDriver");
con = DriverManager.getConnection(url,user, pass);
}
}
(4) getInstancce() 라는 메소드를 생성하고, 3개의 DAO에 있는 connectDB()에서 getInstance()를 호출하게 한다.
<DBcon 클래스>
public Connection getInstance() throws Exception {
DBcon = new DBcon();
return con;
}
<<DAO 클래스>
void connectDB() throws Exception {
DBcon db = new DBcon();
con = db.getInstance();
}
(5) 여전히 DAO에서 new DBcon()을 하면 총 3번 new를 하는 상태이다. 단 한 번만 new를 하기 위해 getInstance()에서 new DBcon하는 조건문을 작성한다.
-> 조건 : con이 없을 때는 con == null으로 알 수 있다.
public Connection getInstance() throws Exception {
if (con == null) new DBcon();
return con;
}
그리고 단 한 번 메모리에 띄운 것을 공유하기 위해 static 처리를 한다.
Connection con
---> static Connection con;
public Connection getInstance() throws Exception
---> public static Connection getInstance() throws Exception
각각의 DAO에서 이미 생성된 con을 얻어오려면 클래스명을 사용하여 메소드를 호출하면 된다.
con = db.getInstance();
---> con = DBcon.getInstance();
(6) 최종적인 DAO의 생성자 함수 형태
public RentDAO () throws Exception {
con = DBcon.getInstance();
}
--> DBcon에서 생성한 con을 DAO들이 공유하여 호출한다.
3. jfreechart
※ www.jfree.org/jfreechart/ 에서 도움말(Docs)을 확인할 수 있다.
※ 차트를 만들고자할 때 사용
(0) DB와 연결 - Database 클래스
<Database 클래스 소스 코드>
package jfreechart;
import java.sql.*;
import java.util.*;
public class Database {
String URL = "jdbc:oracle:thin:@192.168.0.17:1521:orcl";
String USER ="scott";
String PASS = "tiger";
public ArrayList<ArrayList> getData() {
ArrayList<ArrayList> data = new ArrayList<ArrayList>();
try{
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection con = DriverManager.getConnection(URL, USER , PASS);
//***************************************************************
String sql = "SELECT ename, sal FROM emp WHERE sal is not null AND rownum<11 ORDER BY sal DESC";
//***************************************************************
PreparedStatement stmt = con.prepareStatement( sql );
ResultSet rset = stmt.executeQuery();
while( rset.next() ){
ArrayList temp = new ArrayList();
temp.add( rset.getInt("sal")); //****************
temp.add( rset.getString("ename")); //****************
data.add(temp);
}
rset.close();
stmt.close();
con.close();
} catch(Exception ex){
System.out.println("에러 : " + ex.getMessage() );
}
return data;
}
}
(0) 화면 출력을 위해 어떤 그래프를 출력할지 객체를 선택 - MyFrame 클래스
<MyFrame 클래스 소스 코드>
package jfreechart;
import javax.swing.JFrame;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
public class MyFrame extends JFrame {
// 우리가 만드는 화면
MyFrame(){
// *******************************************************
// ChartA demo = new ChartA(); // (1) 정해진 값으로 차트
// ChartB demo = new ChartB(); // (2) DB에서 가져온 값으로 차트
PolylineBarChart demo = new PolylineBarChart(); // (3) bar그래프와 line그래프가 겹쳐진 차트
JFreeChart chart = demo.getChart();
ChartPanel chartPanel=new ChartPanel(chart);
// JFreeChart는 ChartPanel이나 ChartFrame에만 붙일 수 있다.
// 차트만 출력하려면 ChartFrame에 붙여서 바로 출력하거나
// 기존의 화면에 보이게 하려면 ChartPanel에 붙이고 다시 우리 화면 JPanel에 붙인다.
add(chartPanel);
setSize(800,400);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
MyFrame my = new MyFrame();
}
}
(1) 이클립스에서 직접 데이터를 입력하여 차트를 생성하는 경우 - ChartA 클래스
<ChartA 클래스 소스 코드>
package jfreechart;
import java.awt.*;
import org.jfree.chart.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.labels.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.renderer.category.*;
import org.jfree.chart.title.*;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.*;
public class ChartA {
public JFreeChart getChart() {
// 데이터 생성
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
//------------------------------------------------------------------
// 데이터 입력 ( 값, 범례, 카테고리 )
dataset.addValue(1.0, "S1", "1월");
dataset.addValue(4.0, "S1", "2월");
dataset.addValue(3.0, "S1", "3월");
dataset.addValue(5.0, "S1", "4월");
dataset.addValue(5.0, "S1", "5월");
dataset.addValue(7.0, "S1", "6월");
dataset.addValue(7.0, "S1", "7월");
dataset.addValue(8.0, "S1", "8월");
dataset.addValue(5.0, "S1", "9월");
dataset.addValue( 0, "S1", "10월");
dataset.addValue(6.0, "S1", "11월");
dataset.addValue(3.0, "S1", "12월");
// 위 부분을 반복문으로 만들었으면 좋겠다는 강한 느낌 ^^
//------------------------------------------------------------------
// 렌더링 생성 및 세팅
// 렌더링 생성
final BarRenderer renderer = new BarRenderer();
// 공통 옵션 정의
final CategoryItemLabelGenerator generator = new StandardCategoryItemLabelGenerator();
final ItemLabelPosition p_center = new ItemLabelPosition(
ItemLabelAnchor.CENTER, TextAnchor.CENTER
);
final ItemLabelPosition p_below = new ItemLabelPosition(
ItemLabelAnchor.OUTSIDE6, TextAnchor.TOP_LEFT
);
Font f = new Font("Gulim", Font.BOLD, 14);
Font axisF = new Font("Gulim", Font.PLAIN, 14);
// 렌더링 세팅
// 그래프 1
renderer.setBaseItemLabelGenerator(generator);
renderer.setBaseItemLabelsVisible(true);
renderer.setBasePositiveItemLabelPosition(p_center);
renderer.setBaseItemLabelFont(f);
renderer.setSeriesPaint(0, new Color(0,162,255));
// plot 생성
final CategoryPlot plot = new CategoryPlot();
// plot 에 데이터 적재
plot.setDataset(dataset);
plot.setRenderer(renderer);
// plot 기본 설정
plot.setOrientation(PlotOrientation.VERTICAL); // 그래프 표시 방향
plot.setRangeGridlinesVisible(true); // X축 가이드 라인 표시여부
plot.setDomainGridlinesVisible(true); // Y축 가이드 라인 표시여부
// 렌더링 순서 정의 : dataset 등록 순서대로 렌더링 ( 즉, 먼저 등록한게 아래로 깔림 )
plot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
// X축 세팅
plot.setDomainAxis(new CategoryAxis()); // X축 종류 설정
plot.getDomainAxis().setTickLabelFont(axisF); // X축 눈금라벨 폰트 조정
plot.getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.STANDARD); // 카테고리 라벨 위치 조정
// Y축 세팅
plot.setRangeAxis(new NumberAxis()); // Y축 종류 설정
plot.getRangeAxis().setTickLabelFont(axisF); // Y축 눈금라벨 폰트 조정
// 세팅된 plot을 바탕으로 chart 생성
final JFreeChart chart = new JFreeChart(plot);
chart.setTitle(" 우리들의 차트 ");
TextTitle copyright = new TextTitle("월별 입사 현황 ", new Font("SansSerif", Font.PLAIN, 9));
copyright.setHorizontalAlignment(HorizontalAlignment.RIGHT);
chart.addSubtitle(copyright);
return chart;
}
}
(2) 오라클에서 가져온 데이터로 차트를 생성하는 경우 - ChartB 클래스
<ChartB 클래스 소스 코드>
package jfreechart;
import java.awt.Color;
import java.awt.Font;
import java.util.ArrayList;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.CategoryItemLabelGenerator;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.DatasetRenderingOrder;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.HorizontalAlignment;
import org.jfree.ui.TextAnchor;
public class ChartB {
public JFreeChart getChart() {
// 데이터 생성
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
//------------------------------------------------------------------
// 데이터 입력 ( 값, 범례, 카테고리 )
Database db = new Database();
ArrayList<ArrayList> data = db.getData();
for(ArrayList temp : data) {
int value = (Integer) temp.get(0);
String cate = (String) temp.get(1);
dataset.addValue(value, "월별", cate);
}
//------------------------------------------------------------------
// 렌더링 생성 및 세팅
// 렌더링 생성
final BarRenderer renderer = new BarRenderer();
// 공통 옵션 정의
final CategoryItemLabelGenerator generator = new StandardCategoryItemLabelGenerator();
final ItemLabelPosition p_center = new ItemLabelPosition(
ItemLabelAnchor.CENTER, TextAnchor.CENTER
);
final ItemLabelPosition p_below = new ItemLabelPosition(
ItemLabelAnchor.OUTSIDE6, TextAnchor.TOP_LEFT
);
Font f = new Font("Gulim", Font.BOLD, 14);
Font axisF = new Font("Gulim", Font.PLAIN, 14);
// 렌더링 세팅
// 그래프 1
renderer.setBaseItemLabelGenerator(generator);
renderer.setBaseItemLabelsVisible(true);
renderer.setBasePositiveItemLabelPosition(p_center);
renderer.setBaseItemLabelFont(f);
renderer.setSeriesPaint(0, new Color(0,162,255));
// plot 생성
final CategoryPlot plot = new CategoryPlot();
// plot 에 데이터 적재
plot.setDataset(dataset);
plot.setRenderer(renderer);
// plot 기본 설정
plot.setOrientation(PlotOrientation.VERTICAL); // 그래프 표시 방향
plot.setRangeGridlinesVisible(true); // X축 가이드 라인 표시여부
plot.setDomainGridlinesVisible(true); // Y축 가이드 라인 표시여부
// 렌더링 순서 정의 : dataset 등록 순서대로 렌더링 ( 즉, 먼저 등록한게 아래로 깔림 )
plot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
// X축 세팅
plot.setDomainAxis(new CategoryAxis()); // X축 종류 설정
plot.getDomainAxis().setTickLabelFont(axisF); // X축 눈금라벨 폰트 조정
plot.getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.STANDARD);
// 카테고리 라벨 위치 조정
// Y축 세팅
plot.setRangeAxis(new NumberAxis()); // Y축 종류 설정
plot.getRangeAxis().setTickLabelFont(axisF); // Y축 눈금라벨 폰트 조정
// 세팅된 plot을 바탕으로 chart 생성
final JFreeChart chart = new JFreeChart(plot);
chart.setTitle(" 우리들의 차트 ");
TextTitle copyright = new TextTitle("월별 입사 현황", new Font("SansSerif", Font.PLAIN, 9));
copyright.setHorizontalAlignment(HorizontalAlignment.RIGHT);
chart.addSubtitle(copyright);
return chart;
}
}
(3) bar 그래프와 line 그래프가 함께 출력(데이터는 이클립스에서 입력함) - PolylineBarChart 클래스
<PolylineBarChart 클래스 소스 코드>
package jfreechart;
import java.awt.*;
import org.jfree.chart.*;
import org.jfree.chart.axis.*;
import org.jfree.chart.labels.*;
import org.jfree.chart.plot.*;
import org.jfree.chart.renderer.category.*;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.*;
public class PolylineBarChart {
public JFreeChart getChart() {
// 데이터 생성
DefaultCategoryDataset dataset1 = new DefaultCategoryDataset(); // bar chart 1
DefaultCategoryDataset dataset12 = new DefaultCategoryDataset(); // bar chart 2
DefaultCategoryDataset dataset2 = new DefaultCategoryDataset(); // line chart 1
// 데이터 입력 ( 값, 범례, 카테고리 )
// 그래프 1
dataset1.addValue(1.0, "S1", "1월");
dataset1.addValue(4.0, "S1", "2월");
dataset1.addValue(3.0, "S1", "3월");
dataset1.addValue(5.0, "S1", "4월");
dataset1.addValue(5.0, "S1", "5월");
dataset1.addValue(7.0, "S1", "6월");
dataset1.addValue(7.0, "S1", "7월");
dataset1.addValue(8.0, "S1", "8월");
dataset1.addValue(0, "S1", "9월");
dataset1.addValue(0, "S1", "10월");
dataset1.addValue(0, "S1", "11월");
dataset1.addValue(0, "S1", "12월");
// 그래프 2
dataset12.addValue(0, "S2", "1월");
dataset12.addValue(0, "S2", "2월");
dataset12.addValue(0, "S2", "3월");
dataset12.addValue(0, "S2", "4월");
dataset12.addValue(0, "S2", "5월");
dataset12.addValue(0, "S2", "6월");
dataset12.addValue(0, "S2", "7월");
dataset12.addValue(0, "S2", "8월");
dataset12.addValue(6.0, "S2", "9월");
dataset12.addValue(4.0, "S2", "10월");
dataset12.addValue(8.0, "S2", "11월");
dataset12.addValue(7.0, "S2", "12월");
// 그래프 3
dataset2.addValue(9.0, "T1", "1월");
dataset2.addValue(7.0, "T1", "2월");
dataset2.addValue(2.0, "T1", "3월");
dataset2.addValue(6.0, "T1", "4월");
dataset2.addValue(6.0, "T1", "5월");
dataset2.addValue(9.0, "T1", "6월");
dataset2.addValue(5.0, "T1", "7월");
dataset2.addValue(4.0, "T1", "8월");
dataset2.addValue(8.0, "T1", "9월");
dataset2.addValue(8.0, "T1", "10월");
dataset2.addValue(8.0, "T1", "11월");
dataset2.addValue(8.0, "T1", "12월");
// 렌더링 생성 및 세팅
// 렌더링 생성
final BarRenderer renderer = new BarRenderer();
final BarRenderer renderer12 = new BarRenderer();
final LineAndShapeRenderer renderer2 = new LineAndShapeRenderer();
// 공통 옵션 정의
final CategoryItemLabelGenerator generator = new StandardCategoryItemLabelGenerator();
final ItemLabelPosition p_center = new ItemLabelPosition(
ItemLabelAnchor.CENTER, TextAnchor.CENTER
);
final ItemLabelPosition p_below = new ItemLabelPosition(
ItemLabelAnchor.OUTSIDE6, TextAnchor.TOP_LEFT
);
Font f = new Font("Gulim", Font.BOLD, 14);
Font axisF = new Font("Gulim", Font.PLAIN, 14);
// 렌더링 세팅
// 그래프 1
renderer.setBaseItemLabelGenerator(generator);
renderer.setBaseItemLabelsVisible(true);
renderer.setBasePositiveItemLabelPosition(p_center);
renderer.setBaseItemLabelFont(f);
renderer.setSeriesPaint(0, new Color(0,162,255));
// 그래프 2
renderer12.setSeriesPaint(0, new Color(232,168,255));
renderer12.setBaseItemLabelFont(f);
renderer12.setBasePositiveItemLabelPosition(p_center);
renderer12.setBaseItemLabelGenerator(generator);
renderer12.setBaseItemLabelsVisible(true);
// 그래프 3
renderer2.setBaseItemLabelGenerator(generator);
renderer2.setBaseItemLabelsVisible(true);
renderer2.setBaseShapesVisible(true);
renderer2.setDrawOutlines(true);
renderer2.setUseFillPaint(true);
renderer2.setBaseFillPaint(Color.WHITE);
renderer2.setBaseItemLabelFont(f);
renderer2.setBasePositiveItemLabelPosition(p_below);
renderer2.setSeriesPaint(0,new Color(219,121,22));
renderer2.setSeriesStroke(0,new BasicStroke(
2.0f,
BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND,
3.0f)
);
// plot 생성
final CategoryPlot plot = new CategoryPlot();
// plot 에 데이터 적재
plot.setDataset(dataset1);
plot.setRenderer(renderer);
plot.setDataset(1,dataset12);
plot.setRenderer(1,renderer12);
plot.setDataset(2, dataset2);
plot.setRenderer(2, renderer2);
// plot 기본 설정
plot.setOrientation(PlotOrientation.VERTICAL); // 그래프 표시 방향
plot.setRangeGridlinesVisible(true); // X축 가이드 라인 표시여부
plot.setDomainGridlinesVisible(true); // Y축 가이드 라인 표시여부
// 렌더링 순서 정의 : dataset 등록 순서대로 렌더링 ( 즉, 먼저 등록한게 아래로 깔림 )
plot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
// X축 세팅
plot.setDomainAxis(new CategoryAxis()); // X축 종류 설정
plot.getDomainAxis().setTickLabelFont(axisF); // X축 눈금라벨 폰트 조정
plot.getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.STANDARD); // 카테고리 라벨 위치 조정
// Y축 세팅
plot.setRangeAxis(new NumberAxis()); // Y축 종류 설정
plot.getRangeAxis().setTickLabelFont(axisF); // Y축 눈금라벨 폰트 조정
// 세팅된 plot을 바탕으로 chart 생성
final JFreeChart chart = new JFreeChart(plot);
chart.setTitle("Overlaid Bar Chart"); // 차트 타이틀
TextTitle copyright = new TextTitle("JFreeChart WaferMapPlot", new Font("SansSerif", Font.PLAIN, 9));
copyright.setHorizontalAlignment(HorizontalAlignment.RIGHT);
chart.addSubtitle(copyright); // 차트 서브 타이틀
return chart;
}
public static void main(final String[] args) {
PolylineBarChart demo = new PolylineBarChart();
JFreeChart chart = demo.getChart();
ChartFrame frame1=new ChartFrame("Bar Chart",chart);
frame1.setSize(800,400);
frame1.setVisible(true);
}
}