n <- scan()
ss <- scan(what ="") # 문자열 입력
df1 <- data.frame()
df1 <- edit(df1) # data frame 수정창 출력
2 파일 읽기
getwd() # 작업경로 확인. setwd()
list.dirs() # 작업경로의 폴더 정보
list.files() # 작업경로의 파일 정보
stud <- read.table(file = "testdata/student1.txt", header = TRUE) #테이블 read. header(defalut : FALSE)
stud1 <- read.table(file = "testdata/student2.txt", header = TRUE, sep =";") #sep : 구분자
stud2 <- read.table(file = "testdata/student3.txt", header = TRUE, sep =" ", na.strings = "-") # na.strings : 해당 값은 NA로 처리
stud3 <- read.csv(file = "testdata/student4.txt", header = TRUE, na.strings = "-") # read.csv : csv 파일 읽기 (csv 구분자 , 로 된 파일)
class(stud3) # data.frame
datas <- read.csv("https://raw.githubusercontent.com/pykwon/Test-datas-for-R/master/agedata.csv") # 웹 data 가져오기
3 출력
- console
x <- 10; y <- 20; z <- x*y
cat(x,y,z)
cat("결과는", z)
print(x)
print(x,y) # 1개만 출력에 참여
4 자료 저장
sink("output/my.txt") # 저장 선언
datas <- read.csv("https://raw.githubusercontent.com/pykwon/Test-datas-for-R/master/agedata.csv")
head(datas,5)
kbs <- 9
kbs
sink() # 저장 작업 종료
#DataFrame 저장
name <- c("관우","장비", "유비")
age <- c(35,33,20)
gender <- c("M","M","F")
myframe <- data.frame(name, age, gender)
myframe
# name age gender
# 1 관우 35 M
# 2 장비 33 M
# 3 유비 20 F
write.table(myframe, "output/my1.txt") #파일저장
write.table(myframe, "output/my2.txt", fileEncoding ="utf-8")
read.table("output/my1.txt") #파일 불러오기
year <- 2021
is.vector(year) # is.vector() : 벡터인지 확인
name <-'tom'
is.vector(name)
year
year[1] # index가 1부터 시작
seq(1,5) # seq() : 수열 생성
1:5
seq(1,10,2) # 1~10까지 2 간격으로 생성.
seq(1,10,length.out = 4) # 1~10까지 간격이 같은 4개의 수열.
rep(1:3, 3) # 1~3까지 3번 반복. 1 2 3 1 2 3 1 2 3
rep(1:3, times=3) # 동일
rep(1:3, each=3) # 1~3까지 각각 반복. 1 2 3 1 2 3 1 2 3
aa <- c(10,20,30,-10,-5)
x<- c(1,3,5)
y<- c(1:3,1:3)
2 이름 설정
age <- c(10,20,30)
names(age) <- c("홍길동", "김길동", "이길동") # names(x) <- : age에 이름을 설정
age[1] # index로 값 호출
age["홍길동"] # 이름으로 값 호출
age[10] <- 50 # 없는 값은 NA
length(age) # 길이는 10
age <- NULL # NULL값 set
3 우선순위
kk <- c(1, 3, FALSE, T, 3.5, 'hi') # 벡터는 값은 데이터 타입을 가지기 때문에 우선순위에 따라 전체 타입이 정해짐
# 우선순위 : 문자열 > 실수 > 정수 > bool
4 벡터 요소 조회(index) : 슬라이싱
v1 <-c(13,-5,20:23, 12, -2:3)
v1 <- append(v1, 60, after=5) # v1벡터의 5번째 인자 이후에 60이 들어간다.
v1[1] # 첫번째 값. index 1부터 시작
v1[c(1, 3, 6)] # 1, 3, 6 인덱스 값
v1[1:5] # 1~5 인덱스 값
v1[c(3, 5, 1, 8, 9)]
v1[-1] # 1번째 요소를 제외한 나머지를 반환
v1[-c(1, 3, 5)] # 1, 3, 5번째 요소를 제외한 나머지
5 연산
# 하나의 벡터 연산
a <-1:5
a + 5 # 각 요소에 연산적용
a ^ 2 # 제곱
a ** 2 # 제곱
sqrt(a) # 제곱근
sqrt(a) ** 2 # a
# 두 개의 벡터 연산
a <- 1:3
b <- 4:6
a + b # 각 index끼리 연산
a * b
a + 10
a[4] <-2
b[4] <-2
m2[1,] # 1행 전체
m2[,1] # 1열 전체
m2[2,3] # 2행 3열
m2[2,2:4] # 2행 2,3,4열
3 이름 설정
m2 <- matrix(1:12, 3)
colnames(m2) <- c("a", "b", "c", "d")
rownames(m2) <- c("r1", "r2", "r3")
# a b c d
# r1 1 4 7 10
# r2 2 5 8 11
# r3 3 6 9 12
4 연산
a <- matrix(c(1,2,3,4),2,2)
b <- matrix(5:8, 2)
a + b
a - b
a * b
a %*% b # 행렬 곱(내적)
diag(a) # 행, 열 인덱스가 같은 라인의 요소
diag(2)
# 1 0
# 0 1
solve(a) # 역행렬
solve(solve(a))
a %*% solve(a) %*% a
4. List : 서로 다른 타입의 자료를 기억 가능. java의 레코드형 기억장소. c: 구조체.
li <- list('1', '이순신', 70, '2', '한송이', 80)
class(li) # list
unli <- unlist(li) # list에서 되돌리기
num <- list(1:5, 6:10, c("a", "b", "c")) # 1, 2, 3 key에 값을 넣는다.
num[1]; print(class(num[1])) # list
num[[1]]; print(class(num[[1]])) # integer
num[[1]][2] # 1번 list의 index 2번의 값
num2 <- list(x=1:5, y=6:10) # x, y key에 값을 넣는다.
num2$x # num list의 x key의 데이터
num2$y
mem <- list(name='tom', age=22) # name, age key에 값을 넣는다.
a<-list(c(1:5)) # 1~5
b <- list(6:10) # 6~10
result <- lapply(c(a,b), max) # 결과 값을 list로 반환
# [[1]]
# [1] 5
#
# [[2]]
# [1] 10
5. DataFrame : DB의 테이블 구조와 유사. - vetor로 DataFrame 생성
no <- c(1,2,3)
name <- c("tom", "james", "jonh")
pay <- c(300, 400, 500)
df <- data.frame(bunho = no, irum = name, imkum = pay)
#bunho irum imkum
#1 1 tom 300
#2 2 james 400
#3 3 jonh 500
class(df) # data.frame
df[1:2]
df[c(1,3)]
df[2,2]
df[-1,]
df[,-1]
df <- data.frame(irum = c('aa', 'bb', 'cc'), nai = c(22,25,33), row.names = c("one","two", "three"))
df
nrow(df) # 행 수
ncol(df) # 열 수
str(df) # 구조
names(df) # 이름 확인
head(df, n=2) # 앞의 2개 데이터 출력
tail(df, n=2) # 뒤의 2개 데이터 출력
편집 - 전부 실행하기 ctrl + Enter : 한줄 실행 코드 블록 잡기 + ctrl + Enter : 일부분 실행 ctrl + alt + R : 전체실행
ctrl + l : console창 비우기
ctrl + shift + c : 블록영역 주석처리
# : 주석
② 변수 : R에서는 모든 참조형 변수를 사용한다.
- 대입
a <- 1
a=2
3->a
a.kor<-4; # 가능 하나 지양
a_kor<-4
- 출력
cat(a)
cat(a, a.kor) # cat() : 복수의 데이터
print(a, a.kor) # print() : 하나만 출력 - a만 출력
a
(a)
- 변수유형
k <- 9 # 숫자는 기본 double
object.size(k) # object.size() : 객체 크기 - 56 bytes
typeof(k) # typeof() : 타입 - double
k <- as.integer(k) # as.integer() : integer로 type으로 변경
typeof(k)
s <- 5L # L은 정수형 리터럴
typeof(s) # integer
ss <- 'tom' # 문자형
typeof(ss) # character
bb<-TRUE # 논리형 - T
typeof(bb) # logical
- NA, NaN, NULL
aa <- NA # 결측값 : 값으로 인식은 하나 값 자체는 없음.
typeof(aa) # logical
is.na(aa) # is.na() : na 인지 판단.
bb <- NaN # 값으로 인식은 하나 수학적으로 정의되지 않음.
typeof(bb) # double
0 * Inf # NaN
Inf + -Inf # NaN
cc <- NULL # 값으로 인식하지 않음.
typeof(cc) # NULL
sum(2, 3) # 5
sum(2, 3, NULL) # 5
sum(2, 3, NA) # NA
sum(2, 3, NaN) # NaN
- 벡터, factor
ff <-c(2,2,3,2,4) # c() : 벡터 생성.
ff # [1] 2 2 3 2 4
str(ff) # str() : data 구조 확인 num [1:5] 2 2 3 2 4
ff<-factor(ff) # factor() : 범주형 데이터 생성 (레벨을 가지는 배열)
str(ff) # Factor w/ 3 levels "2","3","4": 1 1 2 1 3
# 2,3,4의 레벨을 가지며, 각요소의 레벨 index를 인자로 가진다.
- 함수
func <- function(){ # 함수 생성
return ('good')
}
func() # 함수 호출
typeof(func) # "closure"8
- 데이터 정보 확인
k <-9
cat(k, typeof(k), class(k), mode(k)) # 9 double numeric numeric
# mode() : old type 확인.
# class() : class 확인
str(k) # num 9
is(k) # [1] "numeric" "vector"
- 메모리 clear
ls() # ls() : 메모리에 있는 객체 리턴
ls.str()
rm(kbs) # rm() : 메모리 제거
rm(list=ls()) # 모든 변수 메모리 제거
gc() # 가비지 컬렉터. 점유된 메모리 제거.
- pakage : data + 기능 + 알고리즘 꾸러미 : 라이브러리
available.packages() # available.packages() : 사용 가능한 pakage 목록 확인.
dim(available.packages()) # dim() : 객체의 차원수 확인
# R에서 제공하는 라이브러리 수 확인 (16969)
length(installed.packages()) # installed.packages() : 설치된 package 리스트 .
# 설치된 라이브러 수 확인 (480)
install.packages("plyr") # install.packages() : 라이브러리 설치
library(plyr) # library() : 라이브러리를 load. (import)
# plyr : 연습용 데이터 라이브러리
data(package='plyr') # data(package='') : 라이브러리 데이터 확인.
baseball # plyr column
ozone # plyr column
remove.packages("plyr") # remove.packages() : 라이브러리 삭제
- 데이터 확인
data()
Nile # 나일강의 데이터를 제공하는 라이브러리
head(Nile) # 첫 데이터
tail(Nile) # 마지막 데이터
hist(Nile) # 히스토그램 출력
density(Nile) # 밀도
plot(density(Nile))
- 기타
help("mean") # 도움말
x <- c(0:10, 50)
xm <- mean(x) # 표본평군
c(xm, mean(x, trim = 0.10)) # mean(x, trim = 0.1) : 절사평균. x의 데이터를 크기 순
# 나열 후 0 ~ 0.5의 trim 값에 따라 앞/뒤를 제거 후 평균을 구한다.
<configuration>
<typeAliases>
<typeAlias type="pack.model.JikwonDto" alias="dto"/>
</typeAliases>
<!-- DB 연결을 root-context.xml에서 하도록 수정.-->
<mappers>
<mapper resource="pack/mybatis/DataMapper.xml" />
</mappers>
</configuration>
- DataMapper(xml)
<mapper namespace="dev">
<select id="selectJikwonAll" resultType="dto">
select jikwon_no, jikwon_name, jikwon_jik, jikwon_gen, buser_name
from jikwon left outer join buser
on jikwon.buser_num = buser.buser_no
</select>
<select id="selectLoginData" resultType="dto" parameterType="string">
select jikwon_no, jikwon_name
from jikwon
where jikwon_no=#{no}
</select>
</mapper>
- MyDataSource
public class MyDataSource extends DriverManagerDataSource {
public MyDataSource() {
setDriverClassName("org.mariadb.jdbc.Driver");
setUrl("jdbc:mysql://127.0.0.1:3306/test");
setUsername("root");
setPassword("123");
}
}
<body>
<h2>스프링으로 xml 형태의 결과 반환연습</h2>
<a href="member">출발</a><br>
</body>
=> 요청명 member 전달.(GET방식)
- myform.jsp
<body>
<h2>자료입력</h2>
<form action="member" method="post">
name : <input type="text" name="name" value="tom"><br>
age : <input type="text" name="age" value="22"><br>
<input type="submit">
</form>
<hr>
<form action="member_xml" method="post">
name : <input type="text" name="name" value="tom"><br>
age : <input type="text" name="age" value="22"><br>
<input type="submit">
</form>
</body>
=> 상기 <form>태그 요청명 member 전달.(POST방식)
=> 하기 <form>태그요청명 member_xml 전달.(POST방식)
- MemberController
@Controller
public class MemberController {
@RequestMapping(value="member", method = RequestMethod.GET)
public String formBack() {
return "myform";
}
@RequestMapping(value="member", method = RequestMethod.POST)
@ResponseBody // 자바객체를 Http 응답객체로 클라이언트에 전송 가능.
public String submit(@RequestBody String formData) {
// @RequestBody는 요청 몸체(body)에 있는 client의 요청 값을 그대로 받아들임.
System.out.println("formData : "+formData); // name=tom&age=22
formData+="hello";
return formData; // @ResponseBody 사용 시 formData가 가진 값을 클라이언트에게 바로 송신.
}
...
}
=> @RequestBody : 요청의 Boby 값을 변수에 대입. ex) name=tom&age=22
@Controller
public class MemberController {
...
@RequestMapping(value="member_xml", method = RequestMethod.POST)
@ResponseBody
public XmlMessageList submit_xml(@RequestParam("name") String name,
@RequestParam("age") String age) {
System.out.println(name+" "+age);
return getXml(name, age);
}
public XmlMessageList getXml(String name, String age) {
List<XmlMessage> messages = Arrays.asList(
new XmlMessage(name, age),
new XmlMessage("oscar", "25"),
new XmlMessage("emma", "24")
);
return new XmlMessageList(messages);
}
}
=> 요청명 member_xml(Post방식) 일 경우 실행.
=> @ResponseBody : getXml()의 리턴값을 클라이언트에서 출력한다.
=>
- XmlMessage
@XmlAccessorType(XmlAccessType.FIELD)
//@XmlType(name="", propOrder = {"name","age"})
public class XmlMessage {
private String name;
private String age;
public XmlMessage() {
}
public XmlMessage(String name, String age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
}
- XmlMessageList
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "msg-list") // Root Element 이름 지정
public class XmlMessageList {
@XmlElement(name = "msg")
private List<XmlMessage> messages;
public XmlMessageList() {
}
public XmlMessageList(List<XmlMessage> messages) {
this.messages = messages;
}
public List<XmlMessage> getMessages(){
return messages;
}
}
@Controller
public class SangpumController {
@Autowired
private DataDao dataDao;
@RequestMapping("sangpum")
@ResponseBody // Body를 통채로 송부한다.
public Map<String, Object> abc(){ // JSON은 Map을 리턴해야한다.
List<Map<String, String>> dataList = new ArrayList<Map<String, String>>();
Map<String, String> data = null;
List<SangpumDto> sangList = dataDao.sangpumList(); // DB에서 데이터를 받아온다.
for(SangpumDto s : sangList) {
data = new HashMap<String, String>();
data.put("code", s.getCode()); // key, value 형식으로 DB값을 담는다.
data.put("sang", s.getSang());
data.put("su", s.getSu());
data.put("dan", s.getDan());
dataList.add(data); // List에 Record 값을 넣는다.
}
Map<String, Object> sangpumDatas = new HashMap<String, Object>();
sangpumDatas.put("datas", dataList); // Map에 List를 담아 전송한다.
return sangpumDatas; // JSON은 Map을 리턴해야한다.
}
}
- DataDao
@Repository
public class DataDao extends SqlSessionDaoSupport{
public DataDao(SqlSessionFactory factory) {
setSqlSessionFactory(factory);
}
public List<SangpumDto> sangpumList(){
List<SangpumDto> list = getSqlSession().selectList("selectAll");
return list;
}
}
- SangpumDto
public class SangpumDto {
private String code, sang, su, dan;
//getter/setter
}
@Controller
public class ListController {
@Autowired
private BoardDaoInter boardInter;
private int tot; // 전체 레코드 수
private int plist=5; // 한 페이지 당 행의 수
private int pageCount; // 전체 페이지 수
public ArrayList<BoardDto> getListData(ArrayList<BoardDto> list, int page){
ArrayList<BoardDto> result = new ArrayList<BoardDto>();
int start = (page - 1)*plist; // 해당 페이지의 첫 게시물의 index
int size = plist <= list.size() - start ? plist : list.size() - start;
// 한 페이지에 표시할 데이터가 5개 이하일 경우엔 size가 그 수에 따르도록 조건처리.
for(int i=0; i<size;i++) { // list에 한 페이지당 표시할 데이터만 담아 리턴한다.
result.add(i, list.get(start + i));
}
return result;
}
public int getPageCount() { // get 페이지 수
pageCount = tot/plist;
if(tot%plist > 0 ) pageCount+=1;
return pageCount;
}
@RequestMapping("list")
public Model process(Model model, @RequestParam("page") int page) {
tot = boardInter.totalCnt();
ArrayList<BoardDto> list = boardInter.getList();
ArrayList<BoardDto> result = getListData(list, page);
model.addAttribute("data",result);
model.addAttribute("pageCount",getPageCount());
model.addAttribute("page",page);
//model.addAttribute("data",list); // 페이징 없이 작업할 경우
return model;
}
}
=> totalCnt() :
=> getList() : 전체 List 검색하여 list 리턴.
=> getListData() : 전체 리스트에서 한 페이지 당 표시할 데이터만 리턴값으로 가진다.
=> plist : 한 페이지 당 표시할 레코드 수를 설정.
=> getPageCount() : 페이지의 개수를 리턴.
- BoardBean
public class BoardBean {
private String name,pass,mail,title,cont,bip,bdate;
private int num,readcnt,gnum,onum,nested;
private String searchName, searchValue;
//getter/setter
}
=> FormBean 작성.
- BoardDto
public class BoardDto {
private String name,pass,mail,title,cont,bip,bdate;
private int num,readcnt,gnum,onum,nested;
//getter/setter
}
@Repository
public class BoardDaoImpl extends SqlSessionDaoSupport implements BoardDaoInter {
@Autowired
public BoardDaoImpl(SqlSessionFactory factory) {
setSqlSessionFactory(factory);
}
@Override
public ArrayList<BoardDto> getList() {
return (ArrayList)getSqlSession().selectList("selectList");
}
@Override
public int totalCnt() {
return getSqlSession().selectOne("totalCnt");
}
...
}
=> getList() : board 테이블의 모든 정보 list 리턴.
=> totalCnt() : 레코드 수를 리턴.
<!-- 전체 검색 -->
<select id="selectList" resultType="dto">
select * from board order by gnum desc, onum asc
</select>
<select id="totalCnt" resultType="integer">
select count(*) from board
</select>
=> location.href : 목록버튼 누를 경우 list 출력 창 1 page로 이동.
focus() : 커서가 해당 창으로 이동.
submit() : submit 동작 진행.
- InsertController
@Controller
public class InsertController {
@Autowired
private BoardDaoInter boardInter;
...
@RequestMapping(value="insert", method = RequestMethod.POST)
public String submit(BoardBean bean) {
bean.setBdate();
int newNum = boardInter.currentNum() + 1; // 새로 작성된 글의 번호
bean.setNum(newNum);
bean.setGnum(newNum);
boolean b= boardInter.insert(bean);
if(b) {
return "redirect:/list?page=1";
}else {
return "redirect:/error";
}
}
}
=> POST방식 요청명 insert 받을 경우 실행.
=> <form>태그 데이터 FormBean에 set.
=> setBdate() : Calendar객체를 이용하여 오늘 날짜를 set한다.
=> currentNum() : 현재 테이블의 num값중 가장 큰 값을 받아와 +1하여 현재 num가 group num 값을 set한다.
=> insert() : data 추가.
=> insert 성공 시 전체 list 페이지로 이동, 실패 error 페이지로 이동.
public void setBdate() {
Calendar cal = Calendar.getInstance();
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1;
int day = cal.get(Calendar.DATE);
this.bdate = year + "-" + month + "-" + day;
}
- BoardDaoImpl
@Repository
public class BoardDaoImpl extends SqlSessionDaoSupport implements BoardDaoInter {
@Autowired
public BoardDaoImpl(SqlSessionFactory factory) {
setSqlSessionFactory(factory);
}
...
@Override
public boolean insert(BoardBean bean) {
int result = getSqlSession().insert("insertData",bean);
if(result>0) {
return true;
}else {
return false;
}
}
...
@Override
public int currentNum() {
//insert시 번호 자동 증가를 위해 현재 레코드 중 가장 큰 번호 얻기.
if(getSqlSession().selectOne("currentNum") == null) {
return 0;
}else {
return getSqlSession().selectOne("currentNum");
}
}
...
}
=> insert() : insert에 성공 시 true, 실패 시 false 리턴.
=> currentNum() : 레코드가 없을 경우 현재 가장 큰 num값을 0으로 리턴.
...
<!-- 추가 -->
<insert id="insertData" parameterType="formBean">
insert into board
values(#{num},#{name},#{pass},#{mail},#{title},#{cont},
#{bip},#{bdate},0,#{gnum},0,0)
</insert>
...
<!-- maxnum -->
<select id="currentNum" resultType="integer">
select max(num) from board
</select>
...
=> Data insert
=> board테이블의 num중 가장 큰 값 리턴.
- ErrorController
@Controller
public class ErrorController {
@RequestMapping("error")
public String err() {
return "error";
}
}
@Controller
public class SearchController {
@Autowired
private BoardDaoInter inter;
@RequestMapping("search")
public ModelAndView searchProcess(BoardBean bean) {
ArrayList<BoardDto> list = inter.getSearch(bean);
ModelAndView view = new ModelAndView("list", "data", list);
view.addObject("page","1");
return view;
}
}
=> list.jsp에서 검색 버튼을 누를 경우 searchName(key), searchValue(key)가 전달된다.
=> getSearch() : searchName(key), searchValue(key)에 맞는 값을 검색하여 리스트로 출력한다.
=> 리턴 값을 list.jsp에 출력.
- BoardDaoImpl
@Repository
public class BoardDaoImpl extends SqlSessionDaoSupport implements BoardDaoInter {
@Autowired
public BoardDaoImpl(SqlSessionFactory factory) {
setSqlSessionFactory(factory);
}
...
@Override
public ArrayList<BoardDto> getSearch(BoardBean bean) {
return (ArrayList)getSqlSession().selectList("searchList", bean);
}
...
}
=> getSearch() : 검색 후 ArrayList 리턴.
- DataMapper.xml
...
<!-- 카워드 검색 -->
<select id="searchList" parameterType="formBean" resultType="dto">
select * from board
where ${searchName} like concat('%',#{searchValue},'%')
order by gnum desc, onum asc
</select>
...
=> searchName의 Column에서 searchValue의 키워드가 있을 경우 리스트를 리턴한다.
@Controller
public class DetailController {
@Autowired
private BoardDaoInter inter;
@RequestMapping("detail")
public ModelAndView detailProcess(@RequestParam("num") String num,
@RequestParam("page") String page) {
// 조회수 증가 작업 선행
boolean b = inter.updateReadcnt(num);
// 상세보기 진행 후 jsp로 출력
ModelAndView view = new ModelAndView("detail");
view.addObject("data", inter.getDetail(num));
view.addObject("page", page);
return view;
}
}
=> list.jsp에서 제목을 누를 경우 요청명 detail로 num과 page 값을 전달받는다.
=> @RequestParam : request.getParameter()와 동일.
=> updateReadcnt() : 조회수를 +1한다.
=> getDetail() : 해당 num의 한 레코드 값을 리턴하여 detail.jsp 실행.
-BoardDaoImpl
@Repository
public class BoardDaoImpl extends SqlSessionDaoSupport implements BoardDaoInter {
@Autowired
public BoardDaoImpl(SqlSessionFactory factory) {
setSqlSessionFactory(factory);
}
...
@Override
public BoardDto getDetail(String num) {
// 글 내용보기, 글 수정 시 데이터 받아오기
return getSqlSession().selectOne("selectOne",num);
}
...
@Override
public boolean updateReadcnt(String num) {
//상세보기 전 조회수 증가
int result = getSqlSession().update("updateReadcnt",num);
if(result>0) {
return true;
}else {
return false;
}
}
...
}
=> getDetail() : 게시글의 내용을 받아온다.
=> updateReadcnt() : 업데이트 성공시 true, 실패 시 false 리턴
- DataMapper.xml
...
<!-- 번호로 검색 -->
<select id="selectOne" parameterType="string" resultType="dto">
select * from board where num=#{num}
</select>
...
<!-- readcnt -->
<update id="updateReadcnt" parameterType="string">
update board set readcnt=readcnt + 1
where num=#{num}
</update>
...
<mapper namespace="dev">
<select id="selectAll" resultType="dto">
select num, name, addr from mem
</select>
<select id="selectPart" parameterType="String" resultType="dto">
select num, name, addr from mem
where num=#{num}
</select>
<insert id="insertData" parameterType="formBean">
insert into mem values(#{num}, #{name}, #{addr})
</insert>
<update id="updateData" parameterType="formBean">
update mem set name=#{name}, addr=#{addr}
where num=#{num}
</update>
<delete id="deleteData" parameterType="String">
delete from mem where num=#{num}
</delete>
</mapper>
=> Login에 성공할 경우 redirect로 클라이언트에서 요청을 실행한 것과 같이 실행하여야한다. (forward를 사용하지
않음). Redirect로 list.jsp를 실행한다.
- LoginForm
@Component
public class LoginForm {
private String userid; // <form>태그의 name속성값과 동일하게 변수를 가진다.
private String passwd;
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}
=> <form>태그의 name속성값과 동일하게 변수를 가지도록 작성한다. FormBean은 client의 요청을 처리할 때 사용
한다. DTO는 DB 자료를 처리할 때 사용. (용도에 따라 관례적으로 FormBean과 DTO를 사용한다)
- ListController
@Controller
public class ListController {
@RequestMapping("list")
public ModelAndView aaa() {
ModelAndView view = new ModelAndView("list");
view.addObject("msg", "로그인 성공으로 하고 뭔가를 출력함");
return view;
}
}
@Controller // 클라이언트로 부터 요청 받을 경우 동작
public class ListController {
@Autowired
private DataDao dataDao;
@RequestMapping("testdb") // url mapping
public ModelAndView listProcess() {
ArrayList<SangpumDto> list = dataDao.getDataAll();
return new ModelAndView("list", "datas", list); // list.jsp에 연결.
}
}
=> @Controller : 클라이언트로 부터 요청 받을 경우 동작.
@Autowired : setter injection 기능 수행.
@RequestMapping("url명") : url명과 동일한 url을 받을 경우 실행.
dataDao의 getDataAll()를 실행한 list값을 "datas" key에 넣어 실행한다.
- SangpumDto
public class SangpumDto {
private String code, sang, su, dan;
//getter/setter
}
@Controller // 클라이언트로 부터 요청 받을 경우 동작
public class ListController {
@Autowired
private DataDao dataDao;
@RequestMapping("testdb") // url mapping
public ModelAndView listProcess() {
ArrayList<SangpumDto> list = dataDao.getDataAll();
return new ModelAndView("list", "datas", list); // list.jsp에 연결.
}
}
=> DataSource와 동일
- SangpumDto
public class SangpumDto {
private String code, sang, su, dan;
//getter/setter
}
=> DataSource와 동일
- DataSourceMaria
@Repository() // DB연동
//@Repository("dataSource")
public class DataSourceMaria extends DriverManagerDataSource{
public DataSourceMaria() {
setDriverClassName("org.mariadb.jdbc.Driver");
setUrl("jdbc:mysql://127.0.0.1:3306/test");
setUsername("root");
setPassword("123");
}
}
=> DataSource와 동일
- DataDao
@Repository // DB 연결.
public class DataDao extends JdbcDaoSupport{
//@Autowired
//private DataSourceMaria dataSourceMaria; // DataDao에 주입됨.
//DataSource를 JdbcDaoSupport가 가지고 있으로 생성자를 이용한다.
public DataDao(DriverManagerDataSource dataSource) {
setDataSource(dataSource);
}
public List<SangpumDto> getDataAll(){
String sql="select * from sangdata";
return getJdbcTemplate().query(sql, new ItemRowMapper());
}
class ItemRowMapper implements RowMapper{
@Override
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
SangpumDto dto = new SangpumDto();
dto.setCode(rs.getString("code"));
dto.setSang(rs.getString("sang"));
dto.setSu(rs.getString("su"));
dto.setDan(rs.getString("dan"));
return dto;
}
}
}
=> extends JdbcDaoSupport : JdbcDaoSupport 상속 받아 기능을 수행한다.
=> @Autowird를 사용하면 DataDao객체에 dataSource가 setter injection됨으로 해당 기능이 정상동작하지않는다.
정상동작을 위해선 상속을 받고있는 JdbcDaoSupport객체에 setter injection을 하여야한다. 생성자를 이용하여
DataDao객체가 생성되면 setDataSource()가 실행되도록 하여 JdbcDaoSupport객체에 dataSource를 setter
=> 공유자원을 관리하는 root-context.xml에서 DB연결을 하도록, Sql문 mapping하는 DataMapper.xml관련 설정을
제외하고 삭제.
=> DataMapper파일과 alias를 정의한다.
- DataMapper.xml
<mapper namespace="dev">
<select id="selectAll" resultType="dto">
select * from sangdata
</select>
<select id="selectSearch" parameterType="formBean" resultType="dto">
select code, sang, su, dan from sangdata
where sang like concat('%',#{searchValue},'%')
</select>
</mapper>
=> sql문과 연결되는 Mapper 파일을 작성한다.(<mapper>태그 내부에 sql문을 형식에 맞춰 작성)
=> <select> : select문 작성 시 사용되는 태그. (id속성 - 외부에서 호출할 이름 / resultType - sql문을 실행한 결과
Type / parameterType속성 - 외부로 부터 받을 매개변수의 타입, #{변수명}에 매개변수가 대입된다.)
=> like concat('%', 검색어, '%') : 검색어가 포함된 결과 반환. ( like %검색어%)
@Controller
//@ComponentScan("pack.model")
public class ListController {
@Autowired // setter injection
private SangpumInter sangpumInter; // 다형성 사용
@RequestMapping("list")
public Model process(Model model){
model.addAttribute("data", sangpumInter.list());
// Model객체는 addAttribe(key, value) 메소드를 사용하여 값을 전달.
return model; // 받은 요청명 그대로 송부한다.
}
}
=> @Controller : Controller 기능 수행 (servlet 동작)
=> "list" url 받을 경우 동작한다. list()메소드를 실행한 결과를 "data" key에 넣어 list.jsp에 리턴한다.
- SangpumDto
public class SangpumDto {
private String code,sang, su, dan;
//setter, getter
}
=> DB연동에 사용되는 데이터는 Dto로 작성.(select)
- SangpumBean
public class SangpumBean {
// formBean : 수정, 삭제 등의 작업이 있다면 code, sang, su, dan도 처리
// 검색용
private String searchValue;
public String getSearchValue() {
return searchValue;
}
public void setSearchValue(String searchValue) {
this.searchValue = searchValue;
}
}
@Controller
//@ComponentScan("pack.model")
public class ListController {
@Autowired // setter injection
private SangpumInter sangpumInter; // 다형성 사용
@RequestMapping("list")
public Model process(Model model){
model.addAttribute("data", sangpumInter.list());
// Model객체는 addAttribe(key, value) 메소드를 사용하여 값을 전달.
return model; // 받은 요청명 그대로 송부한다.
}
}
=> xml방식과 동일
- SangpumDto
public class SangpumDto {
private String code,sang, su, dan;
//setter, getter
}
=> xml방식과 동일
- SangpumBean
public class SangpumBean {
// formBean : 수정, 삭제 등의 작업이 있다면 code, sang, su, dan도 처리
// 검색용
private String searchValue;
public String getSearchValue() {
return searchValue;
}
public void setSearchValue(String searchValue) {
this.searchValue = searchValue;
}
}
// MyBatis SQL mapping interface file
public interface SangpumAnnoInter {
@Select("select * from sangdata")
public List<SangpumDto> selectAllData();
@Select(" select code, sang, su, dan from sangdata where sang like concat('%',#{searchValue},'%')")
public List<SangpumDto> selectSearch(SangpumBean bean);
}
① Client요청을 Controller로 연결하기 ② 공유 객체 사용 ③ 패턴을 사용한 경로 매핑 ④ @annotation을 이용한 Controller호출 ⑤ Spring MVC Project를 사용 ⑥ @RequestParam 사용(get) * Controller 값 처리(기존) : getParameter() * Controller 값 처리(어노테이션 사용) * get 방식은 한글이 안깨지나 post 방식사용시 한글이 깨진다. Encording을 하여 한글 깨짐을 방지한다. ⑦ FormBean, @ModelAttribute 사용(post) * Controller 값 처리(기존) : getParameter() * Controller 값 처리(FormBean 사용) * Controller 값 처리(@ModelAttribute 사용)
=> 어노테이션 사용시 <context:component-scan>를 사용한다. pack package의 어노테이션이 동작하도록 한다.
- HelloController * Controller implemets(상속)을 사용할 경우
public class HelloController implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("veiw1"); // veiw1.jsp에 연결
modelAndView.addObject("message", "hello"); // 모델에 다녀왔다 가정. // key-value 전달.
return modelAndView;
}
}
=> hello-servlet.xml/world-servlet.xml에서 연결한 hello.do 요청 발생 시 실행된다.
=> handlerRequest() 메소드를 오버라이딩하여 request를 처리한다.
=> setVieName("파일명")으로 view1에 연결된다. .hello-servlet.xml 파일의 ViewResover 처리로 /views/view1.jsp파일이
연결된다.
=> addObject(key, value)를 이용하여 값을 전달한다.
* @Controller(어노테이션)을 사용할 경우
@Controller
@RequestMapping("hello.do") // 모든 메소드가 수행된다.
public class HelloController{
@RequestMapping(method=RequestMethod.GET)
//@RequestMapping("hello.do") // 해당 메소드만 실행된다.
public ModelAndView aaa() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("view1");
modelAndView.addObject("message", "hello aaa");
return modelAndView;
}
@RequestMapping(method=RequestMethod.POST)
//@RequestMapping("hello.do")
public ModelAndView bbb() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("view1");
modelAndView.addObject("message", "hello bbb");
return modelAndView;
}
}
=> hello-servlet.xml의 <context:component-scan>태그로 인해 @Controller가 동작한다.
@Controller : 클라이언트 요청에 따라 해당 클래스가 Controller동작을 하게된다.
@RequestMapping("요청명") : 요청명에 해당하는 요청이 들어올 경우 클래스가 실행된다.
@RequestMapping(method=RequestMethod.GET) : Get방식의 요청이 들어올때만 실행하게 한다.
@RequestMapping(method=RequestMethod.POST) : Post방식의 요청이 들어올때만 실행하게 한다.
=> ModelAndView객체를 리턴하도록 하여 값을 전달한다. 메소드명을 임의 지정 가능하다.
- WorldController * Controller implemets(상속)을 사용할 경우
public class WorldController implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("view2"); // veiw1.jsp에 연결
modelAndView.addObject("message", "hi world"); // 모델에 다녀왔다 가정. // key-value 전달.
return modelAndView;
}
}
=> HelloController와 동일
* @Controller(어노테이션)을 사용할 경우
@Controller
public class WorldController{
@RequestMapping({"/world.do","/good*.*","/hi/nice.do"}) // 여러개의 요청을 받을 수 있음.
//@RequestMapping("world.do")
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("view2"); // veiw1.jsp에 연결
modelAndView.addObject("message", "hi world"); // 모델에 다녀왔다 가정. key-value 전달.
return modelAndView;
}
@RequestMapping(value="/korea.do", method=RequestMethod.GET) // GET만 받을 수 있다.
//@RequestMapping({"/korea.do"})// Get과 Post를 모두 받을 수 있다.
public ModelAndView handleRequest2(HttpServletRequest request, HttpServletResponse response)
throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("view2"); // veiw1.jsp에 연결
modelAndView.addObject("message", "hi world korea"); // 모델에 다녀왔다 가정. key-value 전달.
return modelAndView;
}
@RequestMapping(value="/usa.*", method=RequestMethod.POST) // POST만 받을 수 있다.
//@RequestMapping(value="/usa.*", method=RequestMethod.GET) // GET만 받을 수 있다.
//@RequestMapping({"/usa.*"})
public ModelAndView handleRequest3(HttpServletRequest request, HttpServletResponse response)
throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("view2"); // veiw1.jsp에 연결
modelAndView.addObject("message", "hi world usa"); // 모델에 다녀왔다 가정. key-value 전달.
return modelAndView;
}
}
=> @RequestMapping({요청명1, 요청명2,..}) : 배열을 사용하여 여러개의 요청명을 받을 수 있다.
@RequestMapping(value="요청명", method=RequestMethod.GET) : 요청과 method 방식을 설정할 수 있다.
요청명에 * 를 사용하여 복수의 요청을 받을 수 있다.
- view1.jsp / view2.jsp
<body>
hello 결과 : ${requestScope.message}
</body>
=> Controller에서 addObject()메소드로 전달한 value를 key를 사용하여 받을 수 있다. (EL 사용 - ${key})
2) 공유 객체 사용
= spr4_share
- index.html
<body>
클래스 간 자원 공유<br>
<a href="hello.do">요청 1</a><br>
<a href="world.kor">요청2</a><br>
</body>
- web.xml
<!-- WebApplicationContext보다 먼저 수행 : 공유 자원 설정시 필요 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
=> applicationContext.xml에 공유자원을 설정한다.
<!-- WebApplicationContext에 의해 자동 수행 -->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- <load-on-startup> init() 메소드 호출 순서 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>world</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>world</servlet-name>
<url-pattern>*.kor</url-pattern>
<!-- <url-pattern>/</url-pattern> 모든 요청사항 -->
</servlet-mapping>
=> *.do 요청과 hello-servlet.xml 연결
=> *.kor요청과 world-servlet.xml 연결
- applicationContext.xml
<!-- 공유 자원 객체 생성 -->
<bean id="sharedData" class="pack.SharedData">
<property name = "shared" value="스프링 프로젝트 공유자원입니다."/>
</bean>
=> setter injection을 이용하여 공유자원 객체 shard에 value를 설정한다.
=>SimSimpleUrlHandlerMapping사용하여 <prop>에 경로 매핑 가능하다.
=> ?는 글자 갯수. * 복수개의 문자를 나타냄.
=> HelloController mapping의 name을 <prop>의 값으로 받아 패턴을 연결한다.
- HelloController
public class HelloController implements Controller{
private HelloModel helloModel;
//setter injection
public void setHelloModel(HelloModel helloModel) {
this.helloModel = helloModel;
}
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// 모델 만들고 모델과 통신하기
String result = helloModel.getGreeting();
ModelAndView modelAndView = new ModelAndView();
//ModelAndView modelAndView = new ModelAndView("hello"); // ViewFile명
modelAndView.setViewName("hello");
modelAndView.addObject("result",result); // forword 방식(default)
//modelAndView.setViewName("redirect:/views/hello.jsp?result='d'"); // reditect 방식
return modelAndView;
}
}
=> Controller를 상속받고 handleRequest 오버라이딩하여 요청사항을 처리한다. => 기본 방식을 forward로 하여 /views/hello.jsp에 addObject(key, value)로 값을 전달한다. => setViewName("redirect:/views/hello.jsp")으로 redirect 가능하다.
@Controller
public class SangpumController {
@RequestMapping(value="sangpum", method=RequestMethod.POST)
public ModelAndView submit(HttpServletRequest request) {
String sang = request.getParameter("sang");
String su = request.getParameter("su");
String dan = request.getParameter("dan");
System.out.println(sang+" "+" "+su+" "+dan);
ModelAndView view = new ModelAndView();
view.setViewName("result");
view.addObject("data","good");
return view;
}
}
* Controller 값 처리(FormBean 사용)
@Controller
public class SangpumController {
@RequestMapping(value="sangpum", method=RequestMethod.POST)
public ModelAndView submit(SangpumBean bean) { // 지역 변수
//클라이언트로 부터 들어오는 데이터 양이 많을 경우 사용(form bean)
System.out.println(bean.getSang()+" "+" "+bean.getSu()+" "+bean.getDan());
ModelAndView view = new ModelAndView();
view.setViewName("result");
view.addObject("data","good");
return view;
}
}
=> 매개변수로 FormBean을 사용하여 값을 전달한다. bean의 범위는 {}안으로 지역변수로 사용된다.
* Controller 값 처리(@ModelAttribute 사용)
@Controller
public class SangpumController {
@Autowired // setter injection
private SangpumModel sanpumModel;
@RequestMapping(value="sangpum", method=RequestMethod.POST)
//public ModelAndView submit(SangpumBean bean) { // 지역 변수
public ModelAndView submit(@ModelAttribute("bean") SangpumBean bean) { // 전역변수
//클라이언트로 부터 들어오는 데이터 양이 많을 경우 사용(form bean)
System.out.println(bean.getSang()+" "+" "+bean.getSu()+" "+bean.getDan());
ModelAndView view = new ModelAndView();
view.setViewName("result");
view.addObject("data",sanpumModel.computeSangpum(bean));
return view;
}
}
=> 매개변수로 @ModelAttribute을 사용시 bean의 범위는 전역변수로 사용되어 addObject() 메소드를 사용하지않아도
다른 파일에서 접근이 가능하다.
- SangpumBean
@Component
public class SangpumBean { // form bean
private String sang; // 클라이언트로 부터 전달되어 오는 요청 Parameter값과 변수명을 동일하게 가져간다.
private int su, dan;
public String getSang() {
return sang;
}
public void setSang(String sang) {
this.sang = sang;
}
public int getSu() {
return su;
}
public void setSu(int su) {
this.su = su;
}
public int getDan() {
return dan;
}
public void setDan(int dan) {
this.dan = dan;
}
}
=> Form Bean 정의. Form Bean의 멤버변수는 <form>태그 하위 태그 name의 값과 동일하게 가져간다.
- SangpumModel
@Component // 객체생성
public class SangpumModel { // 비지니스 로직 처리를 하는 모델 영역의 클래스
public String computeSangpum(SangpumBean bean) {
int price = bean.getSu() * bean.getDan();
String data = "품명" + bean.getSang() + " , 금액은 "+price;
return data;
}
}
- result.jsp
<body>
post 결과 : ${data}
<br>
${bean.sang}
</body>
public class StartController implements Controller{
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ModelAndView : DB 처리 결과를 기억, 출력을 위한 view 파일명 기억
System.out.println("StartController handleRequest()수행");
//return new ModelAndView("list"); // 생성자
//모델과 통신하기 생략
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("list"); // setter. forward 방식으로 파일 요청 처리 (default)
//request.setAttribute("key", value); // servlet에서 사용하는 방법.
modelAndView.addObject("say", "messages");//모델과 통신 후 얻어진 결과를 기억했다 가정.(request)
return modelAndView;
}
}
- list.jsp
기존
<%
String str = (String)request.getAttribute("say");
out.println(str);%>
EL
${say} ${requestScope.say}
@Controller // Conponent와 동일. Cotroller에 사용.
public class StartController{
@RequestMapping("start.do")
public ModelAndView handleStart(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ModelAndView : DB 처리 결과를 기억, 출력을 위한 view 파일명 기억
System.out.println("StartController handleStart()수행 - annotaion");
//return new ModelAndView("list"); // 생성자
//모델과 통신하기 생략
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("list"); // setter. forward 방식으로 파일 요청 처리 (default)
//request.setAttribute("key", value); // servlet에서 사용하는 방법.
modelAndView.addObject("say", "messages - annotaion");//모델과 통신 후 얻어진 결과를 기억했다 가정.(request)
return modelAndView;
}
}
<!-- DriverManagerDataSource를 상속받은 db 연결 class 객체 생성. -->
<bean id="dataSource" class="pack.model.DataSourceMaria"></bean>
<!-- db연결 클래스 객체 생성 -->
<bean id="sangpumImpl" class="pack.model.SangpumImpl">
<!-- JdbcDaoSupport을 상속받았기 때문에 별도로 dataSource setter를 정의하지않아도 setter주입을 할 수 있다. -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 객체생성 -->
<bean id="businessImpl" class="pack.business.BusinessImpl">
<property name="sangpumInter" ref="sangpumImpl"/>
</bean>
public interface HelloInter {
void hello();
void hi();
}
- HelloInterImpl
public class HelloInterImpl implements HelloInter{
public HelloInterImpl() {
System.out.println("HelloInterImpl 생성자");
}
public void hello() {
System.out.println("hello 처리");
}
public void hi() {
System.out.println("hi 처리");
}
}
- WriteInter
public interface WriteInter {
void write();
}
- WriteInterImpl
public class WriteInterImpl implements WriteInter{ // 핵심 로직을 가진 클래스
public WriteInterImpl() {
System.out.println("WriteInterImpl 생성자");
}
public void write() {
System.out.println("write 수행");
}
}
- MyAspect
public class MyAspect { // 관심사항을 가진 클래스
public void myLogOn() {
System.out.println("핵심 메소드 수행전 로그온 작업");
}
public void myLogOut() {
System.out.println("핵심 메소드 수행후 로그아웃 작업");
}
public void mySecurity() {
System.out.println("핵심 메소드 수행전 보안설정");
}
}
- AspectProcess
public class AspectProcess { // 여러개의 관심사항들을 모아 처리하는 클래스
private MyAspect myAspect; // 관심사항 클래스. 여러개 가능
public void setMyAspect(MyAspect myAspect) {
this.myAspect = myAspect;
}
public Object logging(ProceedingJoinPoint joinPoint) throws Throwable{
Object object = null;
myAspect.myLogOn();
object = joinPoint.proceed(); // 메타파일에서 설정한 핵심 메소드 수행.
myAspect.myLogOut();
return object;
}
public Object logging2(ProceedingJoinPoint joinPoint) throws Throwable{
Object object = null;
myAspect.mySecurity();
object = joinPoint.proceed(); // 메타파일에서 설정한 핵심 메소드 수행.
return object;
}
}
=> 핵심 로직의 wri로 시작하고 메개변수가 0개 이상인 메소드가 호출될때 AspectProcess(Aspect를 처리하는) 클래스의 logging() 메소드가 실행된다.
=> 핵심 로직의 hel로 시작하고 메개변수가 0개 이상인 메소드 또는 hi로 시작하는 메소드가 호출될때 AspectProcess클래스의 logging2() 메소드가 실행된다.
- ArticleInter
public interface ArticleInter {
void select();
}
- ArticleDao
//@Component
//@Repository
@Repository("articleDao")
public class ArticleDao implements ArticleInter{
public void select() {
System.out.println("db의 상품 자료를 read");
}
}
- LogicInter
public interface LogicInter {
void selectData_process();
}
- LogicImpl
public class LogicImpl implements LogicInter{
private ArticleInter articleInter;
public LogicImpl() {
}
public LogicImpl(ArticleInter articleInter) {
this.articleInter = articleInter;
}
public void selectData_process() {
System.out.println("selectData_proces 수행");
articleInter.select();
}
}
@Service
public class LogicImpl implements LogicInter{
@Autowired
//@Qualifier("aticleDao")
private ArticleInter articleInter;
public void selectData_process() {
System.out.println("selectData_proces 수행");
articleInter.select();
}
}
- ProfileAdvice
public class ProfileAdvice { // Advice, Aspect
public Object abc(ProceedingJoinPoint joinPoint) throws Throwable{
//핵심 메소드 이름 얻기
String methodName = joinPoint.getSignature().toString();
System.out.println(methodName+" 시작 전 작업");
Object object = joinPoint.proceed(); // 핵심 메소드
System.out.println(methodName+" 처리 후 작업");
return object;
}
}
@Aspect
@Component
public class ProfileAdvice { // Advice, Aspect
@Around("execution(public void selectData_process())")
public Object abc(ProceedingJoinPoint joinPoint) throws Throwable{
//핵심 메소드 이름 얻기
String methodName = joinPoint.getSignature().toString();
System.out.println(methodName+" 시작 전 작업");
Object object = joinPoint.proceed(); // 핵심 메소드
System.out.println(methodName+" 처리 후 작업");
return object;
}
}
- main
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("aop2.xml");
LogicInter inter = (LogicInter)context.getBean("logicImpl");
inter.selectData_process();
}
=> 핵심로직의 selectData_process()메소드 호출 시 profileAdvice클래스의 abc()메소드가 호출.
<!-- 객체 생성 : 어노테이션 -->
<context:component-scan base-package="aop3"/>
<!-- Aop 설정 -->
<aop:aspectj-autoproxy/>
* 스프링 AOP에서 용어
- Joinpoint : ‘클래스의 인스턴스 생성 시점’, ‘메소드 호출 시점’ 및 ‘예외 발생 시점’ 과 같이 어플리케이션을 실행할 때 특정 작업이 시작되는 시점을 의미.
- Advice : 조인포인트에 삽입되어 동작할 수 있는 코드를 말함.
- Pointcut : 여러 개의 조인포인트를 하나로 결합한(묶은) 것을 말함.
- Advisor : 어드바이스와 포인트컷을 하나로 묶어 취급한 것을 말함.
- Weaving : 어드바이스를 핵심 로직 코드에 삽입하는 것을 말함.
- Target : 핵심 로직을 구현하는 클레스.
- Aspect : 여러 객체에 공통으로 적용되는 공통 관점 사항을 말함.
1) AOP 구현
- LogicInter
public interface LogicInter {
void startProcess();
}
- LogicImpl
@Service // @Component와 동일하지만 가독성을 위해 Service기능을 사용하면 @Service를 사용.
public class LogicImpl implements LogicInter{
public LogicImpl() {
}
public void startProcess() {
System.out.println("핵심 로직 수행");
}
}
- aop4.xml
<!-- 어노테이션을 사용하여 객체 생성 -->
<context:component-scan base-package="aop4"/>
<!-- aop를 사용하도록 설정 -->
<aop:aspectj-autoproxy/>
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("aop4.xml");
LogicInter inter = (LogicInter)context.getBean("logicImpl");
// getBean의 매개변수 : @객체 생성할 클래스명을 첫글자 소문자로 변경하여 생성.
inter.startProcess();
}
- RunAspect
// 아이디가 같으면 핵심로직 수행, 다르면 수행하지않도록 구현.
@Component
@Aspect // 독립적으로 동작할 관심 로직 클래스에 사용
public class RunAspect {
@Around("execution(public void startProcess())")
// 핵심로직의 메소드를 선택. 해당메소드 호출시 관심 메소드 호출.
//@Around는 핵심로직 실행 전/후 모두 기능을 추가할 수 있다.
public Object trace(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("AOP 시작 : 핵심 로직 처리전 id 검증");
System.out.print("input id : ");
Scanner scanner = new Scanner(System.in);
String id = scanner.nextLine();
if(! id.equalsIgnoreCase("aa")) { //equalsIgnoreCase() 대소문자 구분하지않고 비교.
System.out.println("id 불일치하여 작업을 종료합니다.");
return null;
}
Object obj = joinPoint.proceed(); // 핵심 로직 수행.
return obj;
}
}
@Component
@Aspect
@Around("excution(핵심 로직의 메소드)")
public Object 관심메소드명(ProceedingJoinPoint joinPoint) throws Throwable{ .. }
=> id나 name 둘중 무엇을 사용해도 상관없으며, <ref bean="name">에서 name을 호출가능.
② 생성자 호출
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("money_init.xml");
MyInter inter = (MyInter)context.getBean("myProcess");
inter.inputMoney();
inter.showResult();
}
}
=> ApplicationContext 객체 생성시 xml에 연결된 모든 생성자가 호출되며, 각 객체가 생성된다.
=> context의 getBean()메소드로 객체를 get하여 메소드를 사용한다.
=> getBean() 메소드의 리턴값은 Object로 다형성을 사용하여 캐스팅하여 객체를 대입한다.
=> getBean() 리턴 객체도 다형성을 사용하여 interface 객체에 대입하여 사용하는 것을 지향한다.
③ MVC 패턴 및 다형성 활용한 프로그래밍
pakage
Controller(Servlet)
Model(Dto, Dao-sql)
Interface
BusinessInterface
ModelInterface
↑
↑
Class
BusinessClass
ModelClass
=> 다형성을 갖춘 MVC Pattern 작성을 지향하여 프로그래밍한다.
=> Controller의 Business Class에서 Model Interface를 호출하여 사용한다.
=> 각 모델은 다형성을 활용하여 테이블에 따른 Sql문을 호출하여 DB와 연결된다.
=> [src.main/java]에 작성한다.
-Business Interface
public interface MyInter {
void inputMoney();
void showResult();
}
-Business Class
public class MyProcess implements MyInter{
private MoneyInter inter;
private int result[];
public MyProcess(MoneyInter inter) {
this.inter = inter;
}
public void inputMoney() {
//키보드로 금액입력
try {
InputStreamReader reader = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(reader);
int myMoney=0;
System.out.println("금액입력");
String data = br.readLine();
myMoney = Integer.parseInt(data);
result = inter.calcMoney(myMoney);
} catch (Exception e) {
System.out.println("inputMoney err "+e);
}
}
public void showResult() {
//계산이 끝난 배열 자료 출력.
System.out.println("만원 : "+result[0]+"개");
System.out.println("천원 : "+result[1]+"개");
System.out.println("백원 : "+result[2]+"개");
System.out.println("십원 : "+result[3]+"개");
System.out.println("일원 : "+result[4]+"개");
}
}
=> 생성자에 모델의 interface 객체를 매개변수로 받아 사용.
- Model Interface
public interface MoneyInter {
int[] calcMoney(int money);
}
- Model Class
public class MoneyCalc implements MoneyInter{
public MoneyCalc() {
}
public int[] calcMoney(int money) {
//단위별 금액 건수 계산용
int result[] = new int[5];
int temp=0;
int len = result.length;
for(int i=0; i<len;i++) {
if(i == 0) {
result[i] = money/(int)Math.pow(10, (len-1-i));
temp=money%(int)Math.pow(10, (len-1-i));
continue;
}
result[i] = temp/(int)Math.pow(10, (len-1-i));
temp=temp%(int)Math.pow(10, (len-1-i));
}
return result;
}
}
③ 생성자 주입(construnctor injection)
- ShowData(Dao)
public class ShowData {
public String toAlias() {
return "홍길동";
}
public String toHobby() {
return "운동";
}
}
- MyProcess(Controller)
public class MyProcess { // 포함으로 약결합 진행
private int age;
private String name;
private ShowData showData; // Class Type
public MyProcess(int age, String name, ShowData showData) {
this.age = age;
this.name = name;
this.showData = showData;
}
public void printData() {
System.out.println(name+age+showData.toAlias()+showData.toHobby());
}
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("anno1.xml");
Abc abc = (Abc)context.getBean("abc");
abc.printData();
}
② @Component : 객체 생성
- SenderInter
public interface SenderInter{
void show();
}
- Sender
@Component // 객체 생성
public class Sender implements SenderInter{
public void show() {
System.out.println("show메소드 수행");
}
}
- Sender2
//@Component = @Component("Sender2")// 객체 생성
@Component("별명")
public class Sender2 implements SenderInter{
public void show() {
System.out.println("Sender2의 show메소드 수행");
}
}
③ @Service : @Component와 동일하나 Business 로직에서 사용한다.
④ @Autowired : setter가 내부적으로 정의되며, type에 의한 mapping 진행.
⑤ @Qualifier("아이디") : type에 대하여 mapping시 동일한 타입(같은 부모를 상속받는 자식) 객체가 복수인 경우
Qualifier의 아이디에 명시한 아이디로 mapping한다.
- SenderProcess
//@Component
//@Service
@Service("SenderProcess") //Component와 동일
public class SenderProcess { // 서비스를 하는 비지니스 로직
private String name = "tom";
@Autowired // xml의 type에 대한 mapping.
//@Qualifier("sender2") // 동일한 타입의 객체(상속받는 자식 객체)가 복수인 경우 id로 mapping
@Qualifier("별명")
private SenderInter senderInter;
//public void setSender(Sender sender) { // @Autowired사용시 삭제가능
// this.sender = sender;
//}
public void displayData() {
System.out.println("displayData의 name : " + name);
senderInter.show();
}
}
- xml
<context:annotation-config/> // @어노테이션을 사용가능하게 한다.
<bean id="senderProcess" class="anno2.SenderProcess"/>
<bean id="sender" class="anno2.Sender"/>
<bean id="sender2" class="anno2.Sender2"/>
// SenderInter를 상속하는 class가 복수가 되면 type으로 mapping하기 때문에 식별자가 필요하다.
<bean class="anno2.Sender"/>
// type에 대한 mapping으로 id가 없어도 mapping된다.
<context:annotation-config/> // 생략 가능
<context:component-scan base-package="anno2"/> // pakage의 @Component, @Service가 작성된 class의 Bean을 생성.
- main
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("anno2.xml");
SenderProcess senderProcess = (SenderProcess)context.getBean("senderProcess");
senderProcess.displayData();
}
public class SangpumModel {
private SqlSessionFactory factory = SqlMapConfig.getSqlSession();
public ArrayList<SangpumDto> getDataAll(){
ArrayList<SangpumDto> list = null;
SqlSession sqlSession = factory.openSession();
list = (ArrayList)sqlSession.selectList("selectDataAll");
sqlSession.close();
return list;
}
}
* Mybatis (@어노테이션)
- db.properties : 계정정보
- Configuration.xml : <alias>, <mapper> 삭제
- DataMapper.xm : 삭제
- SqlMapperInter : 테이블수에 맞게 작성. sql문 작성
public interface SqlMapperInter_Jikwon {
@Select("select jikwon_no, jikwon_name, buser_num, jikwon_jik from jikwon where buser_num = #{buser_no}")
public List<JikwonDto> selectPartJikwon(String buser_no);
}
2) @어노테이션을 이용한 Mybatis 사용: sql문장을 interface의 추상메소드와 연결.
① SqlMapperInter Interface작성 (DataMapper.xml를 대신함 - 삭제)
public interface SqlMapperInter {
@Select("select * from membertab")
public List<DataDto> selectDataAllMember();
@Select("select id, name, passwd, reg_date from membertab where id = #{id}")
public DataDto selectDataPart(String id);
@Insert("insert into membertab values(#{id},#{name},#{passwd},now())")
public int insertData(DataFormBean bean);
@Update("update membertab set name=#{name} where id=#{id}")
public int updateData(DataFormBean bean);
@Delete("delete from membertab where id = #{id}")
public int deleteData(String id);
}
② Configuration.xml 내 DataMapper와 연결된 <mapper>태그 및 alias태그 삭제
③ SqlMapConfig에서
public class SqlMapConfig { // 메뉴얼 확인하여 작성.
public static SqlSessionFactory sqlSession; // DB의 SQL 명령 실행을 위한 메소드를 가진 객체.
static {
String resource = "pack/mybatis/Configuration.xml";
try {
Reader reader = Resources.getResourceAsReader(resource);
// Configuration.xml을 읽는다.
sqlSession = new SqlSessionFactoryBuilder().build(reader);
// sqlSession 객체를 생성한다.
reader.close();
- 하기 code 추가 : sqlSession에 @를 연결.
//Mybatis Annotation을 사용할 경우 추가해야할 코드
Class[] mappers = {SqlMapperInter.class/*,SqlMapperInter2.class ..*/};
for(Class m:mappers) {
//Mapper 등록
sqlSession.getConfiguration().addMapper(m);
}
9. EL (Expression Language) : jsp는 문서내 <tag>와 java code를 혼용되게 사용하여 유지보수에 어려움이 있어
java code의 사용을 줄이기 위해 개발된 언어.(code를 더 단순화 시킴)
1) 연산자
- 산술
${3+4}
${5/4}, ${5 div 4}
${5%4}, ${5 mod 4}
- 관계
${3>4}, ${3 gt 4}
${3<=4}, ${3 le 4}
- 논리
${5>4 and 3<2}
- 3항
${5>4 ? 10:20}
2) life cycle이 다른 내장객체에 따른 데이터 공유
<%
request.setAttribute("a","air"); // 현재 페이지에서 유효
session.setAttribute("b","book"); // 설정시간 동안 유효
application.setAttribute("c","cat"); // 서비스가 유지되는 동안 사용. (지양)
%>
1) 클라이언트의 뷰페이지에서 요청을 보낸다. 2) XMLHttpRequest객체가 생성. 3) 요청처리. 6) 응답받은 메시지를 자바스크립트에서 처리.(응답되는 데이터를 가지고 부분 화면을 변경하기 위해서 javascript와 DOM(JQuey) 사용) 7) 페이지를 업데이트.
4) 서버에서 request를 받는다. 5) request에 대한 처리(DBMS액세스) 후 클라이언트에 응답을 보낸다. (데이터, 문자열, xml, json)
* http : 요청을 하고 응답이 클라이언트에게 전송이 되면 서버에서 발생한 모든 정보가 지워진다.(stateless)
* 상태유지 기술 - 세션 - 쿠키
[내용]
7. 쿠키(Cookie) : 클라이언트에 저장되는 텍스트 조각
client(브라우저)
request ------------> getCookie
server(sevlet)
setAttrubute() ------------>
request객체
getAttribute() ------------>
jsp
setCookie <------------ response
1) setCookie
//Servlet
Cookie cookie1 = new Cookie("name","value"); // Cookie객체 생성 및 데이터 저장
cookie1.setMaxAge(60*60*24); // 쿠키에 유효시간 설정(단위 : 초)
// setMaxAge(-1) : 브라우저 종료시 까지 유지
// setMaxAge(0) : 제거
response.addCookie(cookie1); // response에 쿠키 저장
RequestDispatcher rd = request.getRequestDispatcher("context제외된 경로"); // 요청재지정
rd.forward(request, response);
2) getCookie
//Servlet
Cookie[] cookies = request.getCookies(); //request에서 쿠키정보 꺼내기(배열로 리턴됨)
// 쿠키의 name정보를 이용해서 작업하기
String name = "";
for(int i=0;i<cookies.length;i++) {
if(cookies[i].getName().equals("name")) { // 쿠키의 배열에서 key값인 "name"의 value값 get
name = cookies[i].getValue();
}
}
request.setAttribute("name", name); // 쿠키에서 뺀 데이터를 request에 공유
RequestDispatcher rd = request.getRequestDispatcher("context가 제외된 경로"); // 요청 재지정 - forward
rd.forward(request, response);
// jsp
<%
String name = (String)request.getAttribute("name");
%>
<%= name %>
7. 세션(Session) - 사용자가 브라우저를 키고 사용하는 동안 정보가 유지된다. - 서버메모리에 저장된다. - 내부적으로 쿠키처럼 처리.
1) 세션 사용 ① request에서 세션정보 추출 ② 세션이 없는 경우 -> 세션 만들기 세션이 있는 경우 -> 세션id와 일치하는 세션에 저장된 정보를 확인. ③ 세션 id를 response에 보낸다.
2) 세션객체 만들기 - 세션 id를 request에서 꺼내서 작업하는 것이므로 request객체에서 기존의 세션 정보를 가져오거나 새로
만들어주는 메소드를 제공.
① getSession() - 새로운 세션은 만들때 사용하는 메소드 - request객체에서 세션 id를 추출하는 데 만약 세션이 만들어져 있지않은 상태면 세션을 새로 생성해서 리턴 ex) 로그인하기 위해서 로그인 버튼을 클릭하고 요청되는 로그인 서블릿에서 로그인이 성공하면 세션을 새로
만든다.
② getSession(boolean) - 기존에 작업하는 세션이 있는 지 확인하기 위해서 사용 - getSession(true) - getSession()과 동일하게 동작 - getSession(false) - 기존에 사용하던 세션이 있으면 섹션객체를 리턴하고 없으면 새로만들지 않고 null을 리턴.
//servlet
String id = request.getParameter("id");
HttpSession ses = request.getSession(); //데이터 공유 - 세션
ses.setAttribute("id", id);
RequestDispatcher rd = request.getRequestDispatcher("context가 제외된 경로"); //요청 재지정 - forward
rd.forward(request, response);
//jsp
<%
String id = (String)session.getAttribute("id");
%>
<%= id %>
<tip>
*Chrome 개발자도구 F12 - Application - Cookies : 쿠키 데이터 확인 가능.
public void _jspInit() { ... }
public void _jspDestroy() { ... }
public void _jspService(final javax.servlet.http.HttpServletRequest request,
final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException { ... }
[Servlet 동작] 1) 요청정보 추출 2) 비지니스 메소드 호출 3) 데이터를 공유 4) 요청재지정 5) 서블릿에서 공유한 데이터를 꺼내서 출력하기[jsp]
* html 파일 모든 클라이언트에게 항상 고정된 동일한 화면만 서비스(정적 html문서)
* jsp파일 모든 클라이언트의 상황에 따라 다른 화면을 서비스할 수 있다.(동적 html)
5) 스크립트 요소 ① 스크립트릿 (<% %>)
- 자바코드를 사용할 수 있는 스크립트 요소. - 자바명령문을 정의할 수 있는 곳 - ; 을 문자의 끝에 추가해야한다. - 자바와 동일하게 java.lang패키지는 자동으로 인식하지만다른 패키지를 사용하는 경우 import한다.
- .jsp문서 내에서 선언되는 모든 지역변수를 사용할 수 있다. - jsp문서내 자바코드는 지양해야한다. - 자바코드는 서블릿이 공유하는 데이터를 꺼내서 출력하기 위한 코드만 정의해야한다. - jsp목적이 로직과 뷰를 위한 코드가 혼재되어 있는 부분을 없애기 위해서 만들어진 기술 - 자바코드를 쓰지 않기 위해서 EL & JSTL(표준태그)
<%
// jsp문서에 작성하는 모든 태그나 코드의 작성은_jspService메소드 내부에 만들어진다.
out.println("<h2>hello</h2>"); // out 객체는 자동생성된 객체이므로 사용가능하다.
%>
- 기본형변수, 문자열을 반환하거나 해당 타입으로 선언된 변수를 출력. 변수의 값을 출력하기 위해서 사용. out.print()메소드의 매개변수로 전달되므로 ;을 쓰지않는다. <%= 값 or 값을 리턴하는 메소드 호출문 or 연산식%> - 표현식은 jsp가 서블릿으로 변환될때 out.print() 메소드의 매개변수로 전달되는 코드 - 표현식을 정의할때는 ;을 추가하지않는다. ;을 추가하면 out.print(값;);
- 표현식은 서블릿에서 발생한 값을 웹페이지에 출력할 목적으로 사용하는 jsp스크립트 요소 - 사용가능한 타입이 제한 - 서블릿으로 변환될때 print메소드의 매개변수로 전달되기 때문에 - 기본형, 스트링으로 변환될 수 있는 변수의 값, 메소드의 호출결과, 연산식등을 사용할 수 있다.
2) 이클립스 설정 ① [Package Explorer]창에서 [Project] 오른쪽 클릭 ② 오른쪽 클릭창 - [Build path] - [Configure Build patch] ③ 대화상자에서 세번째 탭인 [Libaries] Tab 선택 ④ [Add External JARs] 선택 후 상기의 오라클 JDBC 위치의 [ojdbc6.jar ] 선택 -> [Apply]
4. JDBC : 자바에서 DBMS 연동을 위한 표준 API(java에서 SQL을 실행하기 위해서 제공되는 표준 API) - driver : 오라클회사에서 자바로 오라클을 접속하기 위해 내부에서 처리해야하는 기능을 구현해 놓은 클래스들을 라이브러리로 만들어 놓은것(ojdbc~.jar)
oracle.jdbc.driver
OracleDriver.class
- API를 이용해서 작업 - 자바 프로그램으로 insert, delete, update, select를 실행.
1) 오라클 드라이버 로딩 [오라클 드라이버명]
oracle.jdc.driver.OracleDriver
java.lang패키지에 Class라는 클래스를 이용해서 오라클 드라이버의 핵심 클래스를 메모리에 로딩 핵심클래스를 드라이버클래스라 하며 드라이버 클래스는 어떤 DBMS를 쓰느냐에 따라서 달라진다.
public static Class<?> forName(String className) throws ClassNotFoundException
Class.forName("패키지를 포함한 오라클 드라이버 클래스명");
Class.forName("oracle.jdc.driver.OracleDriver");
2) DB서버에 연결하기(Connection 설정) => java.sql패키지의 API를 이용 DriverManager의 getConnection메소드를 이용해서 DB서버에 연결.
① static메소드이므로 클래스이름으로 액세스 ② throws하고 있는 SQLException에 대한 처리 ③ 매개변수 url : 연결문자열(접속하기 위한 정보를 하나의 문자열로 만들어 놓은 것, 어떤 DBMS를 쓰느냐에 따라서 다른
현상의 문자열을 작성 - 제조사 홈페이지 참고)
user : 사용자계정 (기본값 : scott) password : 계정 패스워드
[오라클 url]
jdbc:oracle:thin:
@ip:port:
데이터베이스서비스명
프로토콜
DBMS가 설치되어있는 PC의 ip와 port
오라클 설치 시 셋팅하는 값으로 달라질 수 있다.(express버전은 xe)
jdbc:oracle:thin:
@192.168.0.19:1521:
xe
④ 리턴타입 java.sql.Connection이 리턴 DriverManager클래스의 getConnection메소드는 DB서버에 연결하고 연결정보를 객체로 만들어서 리턴한다. 매개변수에 어떤 DBMS를 사용하냐에 따라 다른 Connection객체의오버라이딩 메소드가 실행됨.(다형성 적용)
⑤ 사용방법
Connection con = DriverManager.getConnection(url, user, password);
// oracle.jdbc.driver.T4CConnection 리턴
3) SQL문 실행을 담당하는 객체를 생성 [상속구조]
Statement
: 정적SQL을 실행할 때 사용(보안취약 - SQL Injection공격 취약)
↑
PrspareStatement
: 동적 SQL을 실행할 때 사용(secure coding에 적합 - 정부권고사항)
↑
CallableStatement
: 각 DBMS에 특화된 SQL로 작성된 명령문을 실행 : 오라클 (PL-SQL)
① Statement객체 이용 Connection객체가 갖고 있는 createStatement메소드를 이용
[형식]
Statement stmt = con.createStatement();
//stmt객체는 오라클 드라이버에 포함되어 있는 클래스 객체가 리턴
String sql = "select * from mydept where deptno='"+deptno+"' and deptname ='"+deptname+"'";
* sql insaction
select * from mydept where deptno=002 -- and deptname='인사';
select * from mydept where deptno='' or '1'='1'--
-- 주석문 뒤의 조건문이 다 맞지않아도 검색된다. => Statement는 보안취약
② PreparedStatement객체 이용
- Connection객체의 prepareStatement메소드를 이용해서 생성 - PreparedStatement객체를 만들때 미리 sql문을 파싱 후 외부에서 값만 입력받아서 매핑. - Statement의 단점 보완(보안) - 컬럼명은 입력값으로 처리하지 못한다. - 외부에서 입력받아 처리해야하는값은 ?로 대신해서 sql문을 만든다. 1) sql문을 전달하여 PreparedStatement객체를 생성
String sql = "select * from mydept where deptno = ? and deptname = ?";
PrepaeredStatement ptmt = con.prepareStatement(sql);
2) ?에 값을 셋팅 외부에서 전달받은 값을 ?에 설정해야 sql문이 완성 PrepaeredStatement객체에 있는 set~~~메소드를 이용해서 ?에 값을 셋팅 set~~~메소드는 타입과 연결이 되는 메소드로 ResultSet의 get~~~메소드와 동일한 방법으로 해석
ptmt.setString(A,"B") // A : ?의 순서, B : ?에 셋팅할 값
ptmt.setString(1,"002") // 첫번째 물음표에 문자열 "002"를 셋팅 한다.
// (오라클 타입 : varchar2, char)
4) SQL문 실행 ① Statement를 사용 1)executeUpdate(sql) : insert, delete, update 명령문을 실행. 매개변수로 sql을 전달하면 실행결과로 몇개의 row가 반영되었는 지 리턴.
int result = stmt.executeUpdate(sql명령문);
// 몇개의 행이 삽입, 삭제, 수정되었는 지 리턴.
2)executeQuery(sql) : select 명령문을 실행 실행한 후 조회된 테이블을 리턴, DBMS에서 조회된 테이블을 사용하기 위해 모델링된 자바의 객체가 ResultSet.
실제로는 어떤 DBMS가 사용되었는지에 따라 다른 ResultSet객체가 리턴.
- sql-plus프로그램을 이용해서 select문을 실행할때 결과로 보여지는 2차원 표의 데이터를 자바에서 이용할 수
있도록 모델링한 클래스가ResultSet.
2-1) select문을 실행하면 ResultSet을 리턴하기 때문에 ResultSet타입의 변수를 선언해서 결과를 참조
ResultSet rs = stmt.executeQuery(sql명령문);
2-2) ResultSet에 있는 테이블의 데이터를 읽기 위해서 ResultSet에서 제공하는 메소드를 이용해서 작업 Cursor를 다음 레코드로 이동하면서 레코드 개수만큼 반복작업을 실행. ResultSet에 있는 next()를 호출하면 다음 레코드를 커서를 이동할 수 있다.next()는 커서를 이동했을 때 레코드가
존재하면 true를 리턴하고 없으면 false를 리턴.
while(rs.next()){ // 레코드 개수만큼 실행
// 레코드의 각 컬럼을 읽는다.
}
2-3) 한 번에 하나의 칼럼만 읽을 수 있다. ResultSet의 get~~~메소드를 이용해서 칼럼값을 가져온다. 타입에 따라서 구분 - 오라클의 타입이 varchar2나 char인 경우 : java의 String
getString(칼럼명) or getString(칼럼의 순서 index)
칼럼의 순서 index: 원본 테이블에 정의된 칼럼의 순서가 아니라 조회된 테이블의 칼럼순서로 1부터 시작.
- 오라클 타입이 number(정수) : java의 int getInt(칼럼명) or getInt(칼럼index)
- 오라클 타입이 date : java.sql.Date클래스
while(rs.next()){
// 레코드 개수만큼 실행.
// 커서를 하나씩 다음으로 이동.
// 다음 레코드가 있는 경우 true, 없으면 false리턴
rs.getString("name"); => name컬럼으로 정의되어 있는 칼럼의 입력된 값을 읽어서 출력
rs.getString(2); => 조회된 테이블의 2번째 칼럼의 입력된 데이터를 읽어서 출력
}
서블릿 - 클라이언트의 요청을 처리하기 위한 기술 - 클라이언트가 요청하면 서버에서 실행되며 DB에 대한 처리, 서버의 리소스를 이용해서 만들어진 결과를 클라이언트에 응답한다. - 서버에 의해서 호출되기 때문에 서버가 인식하는 위치에 있어야 하며 서버가 찾아서 실행할 수 있도록 정해진 규칙에 따라 작성되어야한다. (Context/WEB_INF/classes폴더)
1. 서블릿 작성 규칙 1) 표준화된 폴더 구조 안에 서블릿 클래스가 위치해야한다. 서블릿이 저장될 폴더 - classes폴더(서블릿 디렉토리)
[표준화된 폴더 구조]
Context
WEB_INF
web.xml
lib
classes
html, image, css, jsp
=> classes 폴더에 서블릿이 없으면 서버가 찾을 수 없다. (매핑 시엔 가능)
2) 서버에 의해서 호출될 것이므로 반드시 public으로 작성해야한다.
3) 서블릿 클래스가 되기 위해서 무조건 서블릿 클래스를 상속받아야한다.
Servlet(interface)
↑
Generic Servlet(abstact class)
서블릿의 일반적인 내용이 정의된 서블릿 클래스.
↑
destory(), init(), service()
HttpServlet(abstact class)
http프로토콜에 특화된 내용이 정의된 서블릿 클래스.
↑
service(), do~~()
사용자 정의 Servlet
4) 서버가 내부에서 호출할 메소드를 오버라이딩 - 서버가 자동으로 호출하는 메소드를 call back메소드 - 클라이언트가 요청을 하면 서버가 요청을 분석해서 서블릿 디렉토리에 존재하는 (미리 만들어서 저장해놓은) 서블릿 클래스를 찾아서 적절한 상황에 해당 메소드를 자동으로 호출할 것이므로 그 상황에 맞게처리하고 싶은 내용을 각 메소드를 오버라이딩해서 구현한다. - 서버가 서블릿의 life cycle을 관리(객체가 생성돠고 객체를 메모리에서 해제하는 모든 작업) - 오버라이딩할 메소드는 life cycle과 연관되어 있는 메소드
[오버라이딩할 메소드] - init() : 서블릿 객체가 초기화될때 호출되는 메소드. - service() : 클라이언트가 요청할때마다 호출되는 메소드 => 클라이언트의 요청을 처리하는 메소드로 요청을 받고 처리할 내용을 구현. ex) 로그인, 게시판 목록보기, 메일읽기, 장바구니 조회, 구매, 예약, 예약정보 확인...
② 서블릿 매핑 => 어떻게 => 등록된 서블릿을 어떻게 요청할 것인지 등록(어떤 url로 요청할 것인지) => 반드시<servlet>엘리먼트 다음에 위치해야한다. 즉 <servlet>엘리먼트와 함께 한 쌍으로 정의해야한다. => 위에서 등록한 서블릿을 어떻게 요청할 것인지 등록하는 엘리먼트
3) PrintWriter out = resp.getWriter() : 응답정보를 클라이언트로 전송하기 위한 스트림객체를 response에서 구하기 4) 요청정보를 추출 String id = req.getParameter("name") : name(key)으로 값(value) 추출 String[] item = req.getParameterValues("name") : value가 여러개일 경우 배열로 받는다.
5) String str = req.getQueryString() : get방식에서만 사용가능하며, 요청정보의 ? 다음 문자열 전체를 받아온다.
6) ServletInputstream in= req.getInputStream(): post방식에서 사용. 요청정보의 몸체와 연결된 입력스트림을
받아온다.
ServletInputstream in= req.getInputStream();
int len = req.getContextLength();
byte[] buffer = new byte[len];
in.readLine(buffer,0,len);
String str = new String(buffer);
insert into board values(1,'jang','test','test1',0,sysdate,null);
insert into board values(2,'kim','title','ccocnocnco',0,sysdate,'kwdd@naver.com');
insert into board values(6,'choi','oracle','안녕하세요?????',0,sysdate, null);
insert into myemp values('00001', 'scott', sysdate, 3000,'신입');
- 선택 칼럼의 데이터만 입력하기
insert into emp (empno, ename, hiredate) values(1111,'이이이',sysdate);
insert into emp (empno, ename, hiredate, deptno) values(7777,'율곡', sysdate, 100);
2)update
update 테이블
set
where
update board
set title ='oracle'
where no=1;
update board
set title='oracle', hit=hit+1
where no=4;
-set절서브쿼리 이용하여 조건에 맞는 값 update
update myemp
set sal=(select sal
from emp
where empno=7934)
where empno=7369;
update myemp
set sal=(select avg(sal)
from emp
where mgr=7698)
where ename is null;
update myemp
set sal =sal+300
where sal<(select avg(sal)
from emp
where mgr=7698);
-> where절 서브쿼리 이용 조건에 맞는 값 update
3)delete
delete (into) from 테이블명
where
delete from board
where no=5;
delete board
where id is null;
- where절 서브쿼리 이용 조건에 맞는 값 delete
delete from myemp
where sal>(select avg(sal)
from myemp);
4)삽입된 데이터 저장하기
commit;
12. 제약조건
- 테이블 생성시 제약조건 추가
create table 테이블명(
칼럼명 데이터타입(크기) 제약조건,
.... );
create table emp2(id varchar2(10) primary key,
name varchar2(10) not null,
pass varchar2(10));
create table emp2(
empno number(5),
ename varchar2(10) not null,
deptno varchar2(10),
sal number,
tel varchar2(10),
job varchar2(10),
constraints emp2_empno_pk primary key(empno));
- 테이블에 적용된 제약 조건 확인
desc user_contraints
select OWNER,CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME
from user_constraints;
OWNER
---------------------------------------------------------------------------------------------------------------------
CONSTRAINT_NAME CO TABLE_NAME
------------------------------------------------------------ -- -----------------------------------------------------
SCOTT
SYS_C006997 P EMP
SCOTT
SYS_C007004 C EMP2
SCOTT
BOARD_ID_PK P BOARD
SCOTT
EMP2_ID_FK R EMP2
SCOTT
EMP2_SAL_CK C EMP2
SCOTT
SYS_C007013 P DEPT2
SCOTT
EMP2_JOB_CK C EMP2
- 제약 조건 변경 (primary key)
alter table board
add constraint board_no_pk primary key(no);
- 제약 조건 변경 (unique key)
alter table emp2
add constraint emp2_tel_uk unique(tel);
- 제약 조건 변경 (check)
alter table 테이블
add constraint 제약조건명 check(조건);
alter table emp2
add constraint emp2_sal_ck check(sal between 5000 and 7000);
alter table emp2
add constraint emp2_job_ck check(job in('developer', 'siger', 'teacher'));
alter table emp2
add constraint emp2_deptno_fk foreign key(deptno) references dept2(decode);
alter table emp2
add constraint emp2_id_fk foreign key(id) references board(id);
- 제약조건 삭제
alter table emp2
drop constraint emp2_empno_pk;
13. Sequence -시퀀스 생성
create sequence 시퀀스명;
create sequence board_seq;
- 시퀀스 시작값 설정
create sequence tb_order_seq
start with 2020111100;
-현재 시퀀스 확인.
select 시퀀스명.currval form dual;
select tb_order_seq.currval from dual;
->현재 sequence 확인.
- 다음 시퀀스
select board_seq.nextval from dual;
insert into board(no, title,regdate,hit)
values(board_seq.nextval, 'text', sysdate,0);
14. on delete cascade : 미사용 권장
- on delete cascade추가하여 foreign 제약조건 추가.
alter table tb_order
add constraint order_fk foreign key(id) references tb_customer(id) on delete cascade;
-on delete cascadeforeign키 삭제 시 error 발생
drop table tb_customer;
-on delete cascade constraint 삭제
drop table tb_customer cascade constraints;
alter table tb_order
add constraint id_notnull not null(id);
-> error
alter table tb_order
modify id varchar2(10) not null;
-> not null 조건 추가
create table myemp
as
select empno, ename,sal, mgr, hiredate
from emp
where 1=1;
-> 테이블 생성
-> 데이터 들어감
create table myemp2
as
select empno, ename,sal, mgr, hiredate
from emp
where 1=2;
-> 테이블 생성
-> 데이터 안들어감
insert all
into myemp values(7777,'aaaaa',1000,7475,sysdate)
into myemp2 values(7777,'aaaaa',1000,7475,sysdate)
select * from dual;
-> myemp, myemp2에 date insert
insert all
into myemp2 values(1111,'bbbb',1000,7475,sysdate)
into myemp2 values(2222,'ccccc',1000,7475,sysdate)
select * from dual;
-> myemp2에 2개 date insert
9. 서브쿼리(subquery): 여러개의 select 문장을 하나로 합쳐있는것 1) 서브쿼리 - select문에 삽입된 select문, sql문에 삽입된 다른 sql문 - select문에만 사용되지않고 insert, update, delete, select문 모두 사용가능 - 바깥쪽의 쿼리를 메인쿼리 or 기본쿼리라 한다. - sql문에 삽입된 쿼리를 서브쿼리라 하며 반드시 괄호로 묶어 주어야한다. - 서브쿼리가 메인쿼리 실행전에 한번 싱행된다. - 서브쿼리의 결과가 메인쿼리에서 실행된다. - select문에서는 select, from, where, having절 모두에서 삽입가능. 2) 서브쿼리의 종류 ① 단일행 서브쿼리 : 서브쿼리의 실행결과가 행하나, 열 하나로 리턴되는 것, 메인쿼리에서 비교할 때 = 연산자를
이용하는 것이 가능
select ename, sal, hiredate, deptno
from emp
where sal>=(select max(sal)
from emp
where deptno=20);
select *
from emp
where job=(select job
from emp
where ename='SMITH');
select ename, deptno, sal
from emp
where sal>(select max(sal)
from emp
where deptno=20);
select employee_id, first_name||' '||last_name name, hire_date
from employees
where hire_date like '05%'
and hire_date < (select hire_date
from employees
where first_name = 'Lisa');
or
where to(hire_date,'YYYY')='2005'
select e.first_name, e.salary, d.department_name
from employees e, departments d
where e.department_id = d.department_id
and d.department_name = 'Sales'
and salary < (select avg(salary)
from employees
where department_id=100
group by department_id);
select m.employee_id, m.last_name, m.hire_date, m.salary
from employees e, employees m
where e.last_name = 'De Haan'
and e.manager_id = m.employee_id;
select employee_id, last_name, hire_date, salary
from employees
where e.last_name = (select manager_id
from employees
where last_name = 'De Haan');
select d.department_name, avg(e.salary)
from employees e, departments d
where e.department_id = d.department_id
group by d.department_name
having avg(e.salary) <= (select avg(salary)
from employees
where department_id = 30
group by department_id);
② 다중행 서브쿼리 : 서브쿼리의 실행결과가 열은 하나지만 행이 여러개 반환되는 것. = 연산자를 이용할 수 없다. 1) in 연산자
select ename, sal, deptno
from emp
where sal in(select sal
from emp
where job='MANAGER');
select *
from emp
where deptno in (select deptno
from emp
where job='MANAGER');
select ename, empno
from emp
where deptno in (select deptno
from emp
where ename like '%T%');
select *
from emp
where sal in (select min(sal)
from emp
group by deptno);
2)all 연산자 : 모두 만족해야한다. >all(서브쿼리) : 서브쿼리의 모든 값보다 커야함. 서브쿼리의 최대값보다 큰값.
select ename, sal, deptno
from emp
where sal >all(select sal
from emp
where job='MANAGER')
order by sal;
<all(서브쿼리) : 서브쿼리의 최소값보다 작은 값.
select ename, sal, deptno
from emp
where sal <all(select sal
from emp
where job='MANAGER')
order by sal;
3)any : 결과 값 중 한개만 만족하면 됨. >any(서브쿼리) : 서브쿼리의 어느값중 하나만 만족하면 됨. 서브쿼리의 최소값보다 큰 값.
select ename, sal, deptno
from emp
where sal >any (select sal
from emp
where job='MANAGER')
order by sal;
<any(서브쿼리) : 서브쿼리의 최대값보다 작은 값.
select ename, sal, deptno
from emp
where sal <any (select sal
from emp
where job='MANAGER')
order by sal;
③ 다중 열(컬럼) 서브쿼리 : 서브쿼리의 칼럼을 다수로 가져가 비교
select *
from emp
where (deptno, sal) in (select deptno, min(sal)
from emp
group by deptno);
select employee_id, last_name, salary, department_id
from employees
where (department_id, salary) in (select department_id, max(salary)
from employees
group by department_id)
order by salary desc
=> deptno도 같아야 할 경우
=> 칼럼 수가 하나면deptno가 달라도 조회된다.
④ 상호연관 서브쿼리 - 메인쿼리의 칼럼을 서브쿼리에서 사용해야하는 경우. - 메인쿼리의 row가 어떤 칼럼값을 가지고 있느냐에 따라서 서브쿼리의 결과가 달라진다. [실행순서] 1) 메인쿼리에서 현재 작업중인 row의 칼럼값을 가져온다. 2) 메인쿼리에서 가져온 값을 이용해서 서브쿼리를 진행. 3) 서브쿼리에서 실행한 결과를 다시 메인쿼리로 전달 후 메인쿼리를 실행. 4) 메인쿼리에서 사용하는 테이블의 전체 row에 대해서 1~3번까지의 작업을 반복수행.
select ename, sal, deptno
from emp main
where sal > (select avg(sal)
from emp e
where e.deptno = main.deptno);
⑤ View
- view생성 권한부여
conn system/manager
grant create view to scott;
- view생성
create view myempview
as
select d.dname, e.ename, e.sal, e.hiredate
from emp e, dept d
where e.deptno= d.deptno;
- view생성시에는 컬럼에 alias가 꼭 필요.
create view maxsaldata
as
select deptno, max(sal) maxsal
from emp
group by deptno;
create view avgsal
as
select deptno, avg(sal) 평균급여
from emp
group by deptno;
- view 사용
select e.deptno, e.empno, e.ename, e.sal, s.평균급여
from emp e, avgsal s
where e.deptno=s.deptno(+);
⑥ inline view - from절에 정의하고 사용하는 서브쿼리. - 서브쿼리를 이용해서 from절에서 사용할 가상의 테이블을 정의할 수 있는 데, 이를 inline view라 한다. - 하나의 테이블에 데이터가 많이 있는 경우 from정에 정의하면 비효율적일 수 있으므로 필요한 행과 열만
from절에서 사용할 수 있도록 한다. [정의방법]
select
from (select
from
where
group by
having )alias
select e.deptno, e.empno, e.ename, e.sal, avgt.avgsal
from emp e, (select deptno, avg(sal) avgsal
from emp
group by deptno) avgt
where e.deptno = avgt.deptno and
e.sal>avgt.avgsal;
=> from절에 삽입되는 서브쿼리를 사용하는 경우 반드시 서브쿼리 뒤에 alias를 정의해야한다.(테이블명으로 사용)
서브쿼리안에서도 계산의 결과로 만들어지는 칼럼은 반드시 alias를 정의해야한다.(칼럼명으로 사용)
⑦ ton-n 서브쿼리 - row num을 활용해서 순위와 연관있는 서브쿼리.
- rownum사용
select rownum, ename, sal
from emp;
select 월, 입사자수
from (select substr(hire_date,4,2) 월, count(*) 입사자수
from employees
group by substr(hire_date,4,2)
order by 입사자수 desc)
where rownum<4
order by 월;
select to_char(hire_date,'MM') 월, count(employee_id) 입사자수
from employees
group by to_char(hire_date,'MM')
order by 입사자수 desc
select first_name, salary, department_name
from (select e.first_name, e.salary, d.department_name
from employees e, departments d
where department_name = 'IT'
and e.department_id = d.department_id
order by salary desc)
where rownum<4;
select rownum, ename, sal
from emp
where rownum<4
order by sal desc;
->원하는 결과 X
select rownum, ename, sal
from (select *
from emp
order by sal desc)
where rownum <4;
->원하는 결과 O
1) row_number() over (order by '순위 기준 칼럼' desc) -> 동률자 순차적 순번
select row_number() over (order by sal desc) 순위, ename, sal, deptno
from emp;
순위 ENAME SAL DEPTNO
---------- -------------------- ---------- ----------
1 KING 5000 10
2 FORD 3000 20
3 SCOTT 3000 20
4 JONES 2975 20
5 BLAKE 2850 30
6 CLARK 2450 10
7 ALLEN 1600 30
8 TURNER 1500 30
9 MILLER 1300 10
10 ADAMS 1300 20
11 WARD 1250 30
12 MARTIN 1250 30
13 JAMES 950 30
14 SMITH 800 20
15 율곡 500 90
16 이이이 500
2) rank() over (order by '순위 기준 칼럼' desc)
-> 동률자 같은 순번 + 다음 순서 동률자 계산O
select rank() over (order by sal desc) 순위, ename, sal, deptno
from emp;
순위 ENAME SAL DEPTNO
--------- -------------------- ---------- ----------
1 KING 5000 10
2 FORD 3000 20
2 SCOTT 3000 20
4 JONES 2975 20
5 BLAKE 2850 30
6 CLARK 2450 10
7 ALLEN 1600 30
8 TURNER 1500 30
9 MILLER 1300 10
9 ADAMS 1300 20
11 WARD 1250 30
11 MARTIN 1250 30
13 JAMES 950 30
14 SMITH 800 20
15 율곡 500 90
15 이이이 500
3) dense_rank() over (order by '순위 기준 칼럼' desc) -> 동률자 같은 순번 + 다음 순서 동률자 계산
select dense_rank() over (order by sal desc) 순위, ename, sal, deptno
from emp;
순위 ENAME SAL DEPTNO
---------- -------------------- ---------- ----------
1 KING 5000 10
2 FORD 3000 20
2 SCOTT 3000 20
3 JONES 2975 20
4 BLAKE 2850 30
5 CLARK 2450 10
6 ALLEN 1600 30
7 TURNER 1500 30
8 MILLER 1300 10
8 ADAMS 1300 20
9 WARD 1250 30
9 MARTIN 1250 30
10 JAMES 950 30
11 SMITH 800 20
12 율곡 500 90
12 이이이 500
4) ntile('그룹 갯수') over (order by '순위 기준 칼럼' desc) -> 그룹갯수의 수로 균등한 숫자로 순위 줌. 전체 레코드/그룹개수 -> 나머지는 위에 부터 순차적으로 들어감.
select ntile(3) over (order by sal desc) 순위, ename, sal, deptno
from emp;
순위 ENAME SAL DEPTNO
---------- -------------------- ---------- ----------
1 KING 5000 10
1 FORD 3000 20
1 SCOTT 3000 20
1 JONES 2975 20
1 BLAKE 2850 30
1 CLARK 2450 10
2 ALLEN 1600 30
2 TURNER 1500 30
2 MILLER 1300 10
2 ADAMS 1300 20
2 WARD 1250 30
3 MARTIN 1250 30
3 JAMES 950 30
3 SMITH 800 20
3 율곡 500 90
3 이이이 500
5) 위에 합수 over 구문에 partition by '그룹핑' 하여 그 그룹안에서 각각 순위를 정한다.
select rank() over( partition by deptno order by sal desc) 순위, ename, sal, deptno
from emp;
순위 ENAME SAL DEPTNO
---------- -------------------- ---------- ----------
1 KING 5000 10
2 CLARK 2450 10
3 MILLER 1300 10
1 SCOTT 3000 20
1 FORD 3000 20
3 JONES 2975 20
4 ADAMS 1300 20
5 SMITH 800 20
1 BLAKE 2850 30
2 ALLEN 1600 30
3 TURNER 1500 30
4 MARTIN 1250 30
4 WARD 1250 30
6 JAMES 950 30
1 율곡 500 90
1 이이이 500
8. 조인(join): 여러 테이블을 연결해서 필요한 데이터를 조회하는 방법 - 오라클 조인 : 여러테이블 연결조건을 where절에 정의 - ANSI 조인(표준)
1) 개요 : 정규화된 여러 테이블의 데이터를 이용해서 데이터를 조회해야하는 경우 테이블 조인을 한다. - 조인은 관계형 데이터 베이스에서의 중요기능. - 기본키와 외래키 관계를 이용해서 테이블을 조인해야한다. - 조인을 하는 경우 반드시 조인 조건 정의. 2) 조인 사용방법 - from절에 테이블을 정의할 때 alias를 이용해서 정의.
select d.dname, e.ename, e.sal
from emp e, dept d
where e.deptno=d.deptno and sal>2000;
select e.empno, e.ename, e.job, e.deptno, d.deptno, d.dname
from emp e, dept d
where e.deptno=d.deptno;
select d.deptno, d.dname, l.city
from dept d, locations l
where d.loc_code=l.loc_code;
-> deptno : 기본키
- select절에서 두 개 이상의 테이블에 있는 칼럼을 추가하는 경우 from절에서 정의한 alias를 이용해서 칼럼의 모호성 제거. - where절에서는 반드시 조인조건을 정의해야한다. 조인을하면 외래키 테이블의 외래키와 기본키 테이블의 기본키를 비교하고 일치하는 레코드의 원하는 값을 가져오므로 조건을 정의하지않으면 데이터를 조회할 수 없다. - 사용되는 모든 테이블의 조인조건을 정의해야한다. (테이블이 세개면 조인 조건은 두개 정의)
select e.ename, d.dname, e.sal, l.city
from emp e, dept d, locations l
where e.deptno=d.deptno and d.loc_code=l.loc_code;
- 검색 조건 추가 where 조인조건 and 조건추가
select e.empno, e.ename, e.sal, d.dname, d.loc_code
from emp e, dept d
where job='SALESMAN'
and e.deptno=d.deptno;
select ename, sal, hiredate
from emp e, dept d, locations l
where l.city = 'SEOUL'
and e.deptno=d.deptno and d.loc_code=l.loc_code;
select e.ename, e.sal, e.job, e.hiredate, e.comm
from emp e, dept d, locations l
where e.deptno=d.deptno and d.loc_code=l.loc_code
and l.city='DALLAS' and e.sal>=1500;
select d.department_name, count(e.employee_id)
from employees e, departments d
where e.department_id = d.department_id
group by department_name;
select e.first_name || e.last_name || '의 연봉은 ' || e.salary
|| ' 입니다.' as 결과
from employees e,departments d
where e.department_id = d.department_id
and d.department_name = 'IT'
order by salary asc;
select e.employee_id, e.first_name, j.job_title, d.department_name
from employees e, departments d, locations l, jobs j
where e.department_id=d.department_id
and d.location_id = l.location_id
and e.job_id=j.job_id
and l.city ='Seattle';
select j.job_title job, sum(e.salary) 급여
from employees e, jobs j
where j.job_title not like '%Representative%'
and j.job_id=e.job_id
group by j.job_title
having sum(e.salary)>30000
order by sum(e.salary);
select d.department_name 부서명, count(e.department_id) 인원수
from employees e, departments d
where e.department_id = d.department_id
and hire_date <'2005-1-1'
group by d.department_name;
select d.department_id 부서번호, d.department_name 부서명, count(e.employee_id) 인원수,
max(e.salary) 최고급여, min(e.salary) 최저급여, floor(avg(e.salary)) 평균급여, sum(e.salary) 급여총액
from employees e, departments d
where e.department_id = d.department_id
group by d.department_id, d.department_name
having count(e.department_id)>=3
order by 인원수 desc;
select j.job_title job, sum(e.salary) 급여
from employees e, jobs j
where j.job_title not like '%Representative%'
and j.job_id=e.job_id
group by j.job_title
having sum(e.salary)>30000
order by sum(e.salary);
select d.department_name, floor(avg(salary)) 평균연봉
from employees e, departments d
where e.department_id=d.department_id
group by d.department_name
having avg(salary)>= 5000
order by 평균연봉 desc;
3) 조인의 종류(oracle 조인) ① Equip 조인 : 두개 이상의 테이블에서 칼럼 값이 정확하게 일치하는 경우 조회. 조인조건 : where 기본테이블.기본키 = 외래키테이블.외래키 (테이블은 alias 사용가능)
select e.ename d.dname
from emp e, dept d
where e.deptno = d.deptno
② outer 조인 - 조인 적용했을 때 조인 조건을 만족하지않는 데이터를 조회하고 싶을때 사용. - (+) 연산자를 한쪽 칼럼에 초가해서 사용. - 만족하지 않아도 한쪽테이블의 모든 데이터를 조회해서 볼 수 있도록 자원. - (+)가 추가되면 만족되지 않는 조건을 임의로 추가해서 비교하므로 (+)가 투가되지않은 테입르의 레코드가
출력된다. [구문]
select 테이블명1(alias1명).칼럼며으 테이블2 alias2
from 테이블명1 alias1, 테이블명2 alias2
where 테이블명1(alias1).칼럼명(+) = 테이블명2(alias).칼럼명(+)
- dept table에 null을 추가하여 emp table과 비교하여 emp table의 null data를 조회한다.
- Equip 조인은 조인문에 detno가 일치하지않거나 null인 경우 조회되지않는다.
select e.empno, d.dname, e.ename, e.sal
from emp e, dept d
where e.deptno = d.deptno(+);
select nvl(m.ename,'관리자없음') 관리자명, count(e.empno) 인원수
from emp e, emp m
where e.mgr=m.empno(+)
group by m.ename;
select j.job_title, count(e.employee_id)
from employees e, jobs j
where e.job_id=j.job_id(+)
group by j.job_title;
- emp table에 null을 추가하여 dept table과 비교하여 dept table의 null data를 조회한다.
select e.empno, d.dname, e.ename, e.sal
from emp e, dept d
where e.deptno(+) = d.deptno;
select d.department_name, count(e.employee_id)
from employees e, departments d
where e.department_id(+)=d.department_id
group by d.department_name
order by d.department_name;
③ non-Equip 조인(등급표 조인) - 두테이블에서 비교해야하는 칼럼 값이 정확하게 일치하지않고 사이 값인 경우 조인하는 방법. =연산자를
사용하지않은 조인.
select e.empno, e.sal, g.grade
from emp e, salgrade g
where e.sal between g.losal and g.hisal;
④ self 조인 - 같은 테이블에서 조인하는 경우. - 하나의 테이블의 다른 칼럼을 가지고 조인하며 서로 다른 테이블인 것처럼 작업할 수 있다. - 조인조건은 equip조인과 동일하게 정의
select e.empno 사원번호, e.ename 사원명, e.mgr 관리자코드, m.ename 관리자명
from emp e, emp m
where e.mgr=m.empno;
select e.employee_id, e.first_name, nvl(m.first_name,'관리자 없음') 관리자명
from employees e, employees m
where e.manager_id = m.employee_id(+)
and e.first_name like '_t%';
select e.first_name, e.salary
from employees e, employees m
where e.manager_id = m.employee_id
and e.salary>m.salary;
1) 문자처리 함수 ① length() ② sum(), max(), min(), count(), avg(), count(*) ③ lower(), initcap(),upper() ④ dual ⑤ concat(A,B) ⑥ instr('문자열', '찾을 문자',m번째 부터 검색, 찾은 문자의 n번째 위치) ⑦ substr('문자열', m번쨰 부터 짜름, n개 글자) ⑧ replace('문자열', 바꿀문자, 바뀔문자) ⑨ lpad('문자열', 전체 자리수, 공백문자) rpad ⑩ ltrim('문자열', '지울문자') rtrim trim 2) 숫자자리 함수 ① round() ② ceil() ③ floor() 3) 날짜 함수 ① sysdate ② months_between(A,B) ③ add_month(A,B) ④ next_day(A,B) ⑤ last_day() 4) 변환함수 ① to_char(A,'9.99') ② to_char(A,'YYYY-MM-DD') 5) 조건함수 ① decode(칼럼, 값1, 동일하면 출력할 값1 값2, 동일하면 출력할 값2 나머지 경우 출력할 값 ) ② case when 조건식1 then 출력할 값1 when 조건식2 then 출력할 값2 else 나머지 경우 출력할 값 end 별칭 6) null처리 함수 ① nvl(A,B) ② nvl2(A,B)
[내용]
7. 함수 1) 문자처리 함수
① length()
select ename, length(ename)
where length(ename)>=6;
② sum(sal), max(), min(), count(), avg(), count(*)
select ename, sal, max(sal)
from emp
group by ename;
select deptno, count(empno)
from emp
group by deptno;
③ lower(), initcap(첫글자만 대문자),upper()
select empno, ename, lower(job), deptno
from emp
where ename = 'SCOTT'
④ dual : test 1행짜리 테이블.
select *
from dual;
⑤ concat(A,B) : || 문자열 연결. concat(A, concat(B,C))
select concat(ename,concat('의 급여',concat(sal,'만원')))
from emp
where sal<1000;
⑥ instr('문자열', '찾을 문자',m번째 부터 검색, 찾은 문자의 n번째 위치) -> 위치반환
⑦ substr('문자열', m번쨰 부터 짜름, n개 글자) -> 글자 추출 -1은 맨뒤.
select ename, hiredate
from emp
where substr(hiredate,1,2)=81;
select empno, ename, job, sal, deptno
from emp
where substr(ename,1,1) > 'K' and substr(ename,1,1) < 'Y';
⑧ replace('문자열', 바꿀문자, 바뀔문자)
⑨lpad('문자열', 전체 자리수, 공백문자) -> 왼쪽에 공백문자를 가짐, 전체 자리수에서 부족한 공백은 설정한 공백문자로 채운다.
rpad : 오른쪽에 공백문자를 가짐.
select ename, job, lpad(sal,5,'*') as sal
from emp
where sal<=2000;
⑩ ltrim('문자열', '지울문자') : 왼쪽 문자를 지움
rtrim('문자열', '지울문자') : 오른쪽 문자를 지움 trim(''from'문자열') : 양쪽 문자를 지움
select ename, job, ltrim(lpad(sal,5,'*'),'*') as sal
from emp
where sal<=2000;
select ltrim(job,'A'), ltrim(sal,1)
from emp
where deptno=10;
2) 숫자자리 함수 ①round() : 반올림
select deptno, round(avg(sal)) avg
from emp
where job<>'PRESIDENT'
group by deptno
having avg(sal)>1800
order by deptno;
②ceil() : 올림 ③floor : 버림
3) 날짜 함수 ① sysdate : 오늘날짜. 연산가능 ②months_between(최신날짜, 먼날짜) ③add_month(,더할달수) ④ next_day(지정일,'금') 요일 ⑤ last_day(지정일) : 마지막날
select e.first_name, e.salary, e.hire_date, d.department_name
from employees e, departments d
where e.department_id=d.department_id
and months_between(sysdate,hire_date) >12*18;
②case when 조건식1 then 출력할 값1 when 조건식2 then 출력할 값2 else 나머지 경우 출력할 값 end 별칭
select ename, sal, case when sal>=5000 then '1등급'
when sal>=2000 and sal<5000 then '2등급'
when sal>=1000 and sal<2000 then '3등급'
else '신입'
end 등급표
from emp;
6) null처리 함수 ① nvl(칼럼or표현식, null인 경우 적용할 값 or 표현식(연산,함수호출))
select nvl(to_char(department_id),'No Department')부서번호, round(avg(salary),0)평균급여
from employees
group by department_id
having avg(salary) >6000;
② nvl2(칼럼 or 표현식, null이 아닌 경우 적용할 값, null인 경우 적용할 값)
select ename, mgr, nvl2(mgr,'담당','상위자') 관리자
from emp
order by 관리자;