MySQL의 DB에 저장된 데이터가 대략 1분 간격으로 저장된 데이터가 있다고 할때에 이 데이터에서 10분 간격의 데이터를 추출하고 싶을때 쿼리를 어떻게 작성해야 할까?
이를 위해 필요한 기법과 개념은 
 -. UNIX_TIMESTAMP() 함수
 -. GROUP BY
 -. DIV
정도이다.

테이블명 : tb_test
r_Date : 2022-08-01 15:59:00과 같은 형태로 대략 1분 간격의 데이터가 저장되어 있다고 할때

SELECT * FROM tb_test GROUP BY UNIX_TIMESTAMP(r_Date) DIV 600 ORDER BY r_Date DESC

-. UNIX_TIMESTAMP(r_Date)에서 출력되는 결과는 1659337140이다. 이는 1970.1.1이후 경과한 초의 값을 반환한다.

MySQL UNIX_TIMESTAMP() returns a Unix timestamp in seconds since '1970-01-01 00:00:00' UTC as an unsigned integer if no arguments are passed with UNIX_TIMESTAMP().
When this function used with a date argument, it returns the value of the argument as an unsigned integer in seconds since '1970-01-01 00:00:00' UTC.
The argument may be a DATE, DATETIME,TIMESTAMP or a number in YYYYMMDD or YYMMDD.

-. UNIX_TIMESTAMP(r_Date) DIV 600
이것이 의미하는 것은 r_Date 칼럼에 있는 년-월-일 시:분:초의 값을 UTC 초 값으로 반환된 것을 600(10분에 대한 초)으로 나눈 값을 반환한다. 이때 소수점 이하는 절삭된 정수형 값을 반환한다. 

-. GROUP BY UNIX_TIMESTAMP(r_Date) DIV 600
이것의 의미는 1분간격의 년-월-일 시:분:초의 값을 DIV 600으로 나눈 후 소수점을 절삭하면 10분 간격의 시간 값은 동일한 값을 갖게 된다.  DIV 자체가 소수점을 절삭한 정수형을 반환한다.
사실은 2765561.x, 2765560.x, 2765559.x와 같이 소수점 이하에서 각각 다른 값들을 갖는데 소수점이 절삭된 정수형 값을 취하게 되면 아래와 같이 10분 간격의 값들은 동일한 값을 가지므로 GROUP BY로 묶을수 있는 조건이 된다.

2022-08-01 15:59:00;  2765561
2022-08-01 15:58:00;  2765561
2022-08-01 15:57:00;  2765561
2022-08-01 15:56:00;  2765561
2022-08-01 15:55:00;  2765561
2022-08-01 15:54:00;  2765561
2022-08-01 15:53:00;  2765561
2022-08-01 15:52:00;  2765561
2022-08-01 15:51:00;  2765561

2022-08-01 15:48:00;  2765560
2022-08-01 15:47:00;  2765560
2022-08-01 15:46:00;  2765560
2022-08-01 15:45:00;  2765560
2022-08-01 15:44:00;  2765560
2022-08-01 15:43:00;  2765560
2022-08-01 15:42:00;  2765560
2022-08-01 15:41:00;  2765560
2022-08-01 15:40:00;  2765560

2022-08-01 15:39:00;  2765559
2022-08-01 15:38:00;  2765559
2022-08-01 15:37:00;  2765559
2022-08-01 15:36:00;  2765559
2022-08-01 15:35:00;  2765559
2022-08-01 15:32:00;  2765559
2022-08-01 15:31:00;  2765559
2022-08-01 15:30:00;  2765559

따라서 GROUP BY로 묶으면 아래와 같이 10분 간격의 값을 추출할수가 있다. 만일 5분 간격으로 데이터를 추출하고자 한다면 DIV 300으로 하면 된다.

2022-08-01 15:51:00;  2765561
2022-08-01 15:40:00;  2765560
2022-08-01 15:30:00;  2765559

JPA 프로젝트 생성시 Project Facets에서 JPA 항목이 보이지 않는 문제가 있다. 이는 중대한 이슈가 JPA와 관련해서 있기 때문에 이클립스 쪽에서 제거한 것으로 나와있다(자세한 건 여기를 참조)

Help 메뉴
  ⇒ Install new software...
    ⇒ Work with: 항목에 http://download.eclipse.org/releases/oxygen를 입력 후 엔터
      ⇒ 아래 Name 항목 여럿 중에서 
               "Web, XML, Java EE and OSGi Enterprise Development" 항목을 펼쳐서
                JPA 관련 모든 항목을 체크한다
                 (이때 간단히 찾는 방법은 위의 "type filter text" 항목에 JPA라고 입력)
             ⇒ 이렇게 설치과정을 진행 후 이클립스 재구동

이렇게 하면 정상적으로 JPA 항목이 보일 것이다.

특정 웹 페이지 로딩시 프로그레서 바/프로그레스 써클 띄워야 할 경우가 있다면 dialog 태그와 img 태그와 로딩 이미지를 보여주는 gif 이미지를 적절히 조합하여 로딩 상태를 띄워줄수가 있다. 
아래는 코드이다.

<html>
<head>
	<title>프로그레스 써클 띄우기</title>
	<style>

	</style>
</head>
<body>
	<br/>
	<button onclick="showProCircle()" style="padding: 5px;"> 프로그레스 써클 띄우기 </button><br/>
	<h3> 5초 후에 자동으로 닫힙니다. </h3>

	<div>
		<!-- dialog 태그를 border-radius 속성을 이용해서 원형으로 만들고 gif 이미지도 동일하게 원형으로 만든다 -->
		<dialog id="progressCircleDialog" style="width:200px; height:200px; padding:-5px; background-color:#ffffff; border: 0px solid black; border-radius: 50%;">
			<img style="width: 100%; height: 100%; display: block; margin: 0px auto; padding: 0px; border-radius: 50%;" src="loading.gif">
		</dialog>
	</div>
	<script>
		function showProCircle(){
			var dialog = document.getElementById("progressCircleDialog");
			dialog.showModal();

			setTimeout(function(){
				dialog.close();
			}, 5000);
		}
	</script>
</body>
</html>

웹의 view단을 작업할때 alert() 함수를 이용해서 사용자에게 필요한 알림을 제공하게 된다. 가장 간단하고 쉬운 방법이나 alert()을 사용할때 예상치 못한 곤란을 만날수도 있다. 아무튼 alert()과 같은 역할을 하면서 alert()을 대체할수 있는 dialog 태그에 대해 정리하고자 한다.

<html>
<head>
	<title>alert을 대체할 dialog 태그</title>
	<style>
		#myMsgDialog {
			width:40%; 
			background-color: #f4ffef; 
			border:1px solid black; 
			border-radius: 7px;
		}		

		#mButton {
			padding: 7px 30px;
			background-color: #66ccff;
			color: white;
			font-size: 15px;
			border: 0;
			outline: 0;
		}
	</style>
</head>
<body>
	<button onclick="showMsg()">메시지 띄우기</button>

	<div>
		<!-- 아래 dialog 태그 영역이 메시지 창 -->
		<dialog id="myMsgDialog">
			<h3>여기는 메시지 내용입니다.</h3>
			<input type="button" id="mButton" onclick="closeMsg()" value=" 확 인 " >
		</dialog>
	</div>
	<script>
		function showMsg(){
			var dialog = document.getElementById("myMsgDialog");
			dialog.showModal();
		}
		
		function closeMsg(){
			var dialog = document.getElementById("myMsgDialog");
			dialog.close();
		}
	</script>
</body>
</html>

코드가 그렇게 복잡하지는 않다. 아무튼 alert() 대체용으로 간단하면서 아주 유용한 기능이다. 아래는 실행 결과이다.

 

 

Oracle의 ROLLUP 함수의 개념

아래와 같은 테이블이 있다고 할때 부서별 연봉(SAL) 소계와 전체 SAL 총계를 도출해 내고자 할때 사용할수 있는 함수가 ROLLUP 함수이다.
한마디로 말하면 GROUP BY로 묶을 칼럼의 소계를 도출하는 함수이다.

DEPT      |  RANK  |  SAL
--------------------------------
기획부    |  부장   | 3800
기획부    |  부장   | 4000
기획부    |  차장   | 1800
마케팅부 부장   | 4000
마케팅부 |  차장   | 2000
마케팅부 |  과장   | 1500

SELECT DEPT, RANK, COUNT(RANK) "RANK_COUNT", SUM(SAL) FROM TEST GROUP BY ROLLUP(DEPT, RANK);

위 쿼리가 의미하는 바는 DEPT와 RANK라는 두 칼럼의 데이터가 같은 튜플들을(row) 하나로 묶어(grouping) SAL의 합을 도출하고
DEPT 칼럼의 데이터가 같은 튜플들을 하나로 묶어 SAL의 소계를 도출하고 최종적으로는 전체 튜플들 모두의 합(총계)를 도출하는 기능을 하는 것이 ROLLUP() 함수이다.

DEPT    |  RANK  |  RANK_COUNT | SAL
------------------------------------------------------------------------------
기획부    |  부장  | 2                   | 7800 ==> 기획부, 부장의 합계
기획부    |  차장  | 1                   | 1800
기획부    |  (null) | 3                   | 9600 ==> 기획부 소계
마케팅부  과장  | 1                   | 1500
마케팅부  부장  | 1                   | 4000
마케팅부  차장  | 1                   | 2000
마케팅부  (null) | 3                   | 7500 ==> 마케팅부 소계
(null)         (null) | 6                   | 17100 ==> 전체 총계

 

PHP에서 MVC 모델 형태로 웹 시스템을 개발하다보면 DB에 저장, 혹은 DB 정보를 수정하는 URL이 생성되는데 문제는 해당 URL을 막바로 웹브라우저 주소 창에 입력하게 될 경우 문제가 발생하게 된다.
DB에 insert하는 경우의 URL을 웹 브라우저에서 막바로 입력해서 막바로 접속할 경우 시스템을 정교하게 만들지 않을 경우 데이터가 없는 새로운 레코드가 생성되는 불상사가 발생하게 된다.

http://xxx.xxx.xxx/adm/reg/member 
  ⇒ 회원가입 페이지(reg_member.php)
    ⇒ http://xxx.xxx.xxx/adm/save/member
       ⇒ DB에 회원 정보 insert(AdmModel.php -> insertMember())

여기서 사용자가 웹 브라우저 주소창에  http://xxx.xxx.xxx/adm/save/member 이 URL로 막바로 접속할 경우 AdmModel.php의 insertMember() 함수가 실행되어 DB에 입력 값이 없는 회원 레코드가 하나 생성되는 불상사가 발생한다는 것이다.
따라서 이런 경우를 막기 위해서는 아래와 같이 간단한 몇줄의 코드로 방어할수 있다.

        public function registeMember($regMbr){
            $prevPage = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_PATH);

            if($prevPage != '/adm/reg/member'){
                echo "<script>alert('허용되지 않는 잘못된 접근입니다.');</script>";
                return;
            }
        }

HTTP_REFERER 환경 변수는 이전 페이지의 URL 값을 담고 있는 환경변수이다. 따라서 정상적인 경우라면 /adm/save/member URL로 들어오기 전 URL은 /adm/reg/member 이어야 하는 것이다. 이 값이 아닌경우라면 차단하면 되는 것이다.
즉 브라우저에서 회원 정보를 등록하는 URL인 http://xxx.xxx.xxx/adm/save/member로 막바로 접속시 위의 $prevPage에는 아무 값이 없다. 따라서 그런 경우는 차단을 하면된다.

PHP에서 MVC 모델 방식에서 Controller 기능을 구현할려면 어떻게 해야 할까? 혹은 FrontController 기능을 구현할 경우 어떻게 해야 할 것인가?
php의 경우는 url 경로에 맞는 해당 경로에 php 소스가 1:1로 대응되도록 되어 있다.

http://xxx.xxx.xxx/reserve/list.php

일 경우 웹 Document Root 디렉토리 아래의 reserve 디렉토리 안에 list.php소스가 있어야 되는 식이다.
문제는 이런 방식으로 동작하기 때문에 어느 특정 디렉토리의 특정 php 소스를 Controller 역할을 하게 할수가 없게된다. 달리말해서 어떤 형태의 url이 들어오더라도 무조건 특정 디렉토리의 특정 php 소스를 무조건 실행되게 할수가 없다는 것이다.
어떤 경우의 url 형태가 요청되더라도 항상 특정 php가 실행되어야 해당 php 소스가 Controller 역할을 수행할수 있는것 아니겠는가?

또 url 경로에 맞는 디렉토리에 해당 소스가 있다는 것은 그 소스가 노출될 위험성을 안고 있는 보안상의 문제가 있게 된다.

PHP의 경우 이러한 난점을 해결하고 모든 url에 대해 특정 php로 redirect 시켜 해당 php가 FrontController 역할을 하도록 하기 위해 apache의 기술을 이용하면 Controller 기능을 구현할수 있다.
이때 사용하는 것이apache의 rewrite 모듈이다. 

apache의 rewrite 모듈은 apache의 모듈의 한 종류로 서버로 접속해 오는 request를 정해진 규칙을 통해 특정 경로의 파일로 redirect 시키는 역할을 하는 모듈이다.
아래의 절차를 따라 진행하면 된다(Ubuntu 16.04를 기준으로 작성된 글이다).

(1) Apache rewrite 모듈 활성화
아래 명령어를 통해 apache rewrite 모듈을 활성화 한다.

# a2enmod rewrite

(2) Apache 설정에서 rewrite 기능을 사용가능하도록 설정 변경
아래의 경로에 있는 apache2.conf 파일을 열어 설정을 변경한다.

# vi /etc/apache2/apache2.conf

<Directory /var/www/>
	Options Indexes FollowSymLinks
	AllowOverride None
	Require all granted
</Directory>

위의 AllowOverride None을 AllowOverride All로 변경

(3) .htaccess 파일에서 규칙 생성
웹 Document Root 디렉토리에 .htaccess 파일을 생성한다. apache2의 웹 document root가 어디인지 확인할려면 Ubuntu 16.04의 경우 /etc/apache2/sites-available/000-default.conf 파일을 열면 아래와 같은 내용이 있을 것이다.

DocumentRoot /var/www/html

따라서 .htaccess 파일을 /var/www/html/ 에 생성해서 아래의 내용을 입력한다.

RewriteEngine On
RewriteBase /
RewriteRule ^([^.?]+)$ /ebook3/index.php

.htaccess 파일을 작성하는 규칙은 복잡한 많은 내용이 있지만 여기서는 최소한으로 간략히 표현 경우이다.
위의 설정 내용의  /ebook3/index.php에서 /가 의미하는 것은 어느 경로 위치를 의미하는가 하는 것이다. 즉 여기서 /의 위치는 어디이냐 하는 것이다.
여기서 /ebook3/index.php의 위치는 Web DocumentRoot의 경로 위치인 /var/www/html/ebook3/index.php를 의미한다. 따라서 /가 의미하는 것은 Web DocumentRoot의 위치를 의미한다.

위의 내용은 어떤 형태의 url로 접근을 하더라도 무조건 /ebook3/index.php로 redirect 시키라는 규칙이다.
만일 
http://xxx.xxx.xxx/reserve/list
http://xxx.xxx.xxx/reserve/view
http://xxx.xxx.xxx/reserve/list/json
...
과 같이 url이 reserve로 시작하는 모든 경우 /ebook3/index.php로 이동시키고 싶다면 RewriteRule을 다음과 같이 하면 될 것이다.

RewriteRule ^reserve /ebook3/index.php

(4) apache2  재시작
이상의 모든 설정 사항이 적용되도록 아파치 재 구동

# service apache2 restart

rewrite 모듈이 정상적으로 구동중인지 확인할려면 phpinfo() 를 통해 나오는 페이지에서 "Loaded Modules" 항목을 보면 mod_rewrite라는 항목이 있으면 해당 모듈이 정상 동작하고 있는 것이다.

이상의 작업이 완료되면 이제 PHP에서 MVC 모델을 적용하되 FrontController 역할을 index.php로 할수 있게된다. 즉 접속해 오는 모든 url을 무조건 index.php로(혹은 index.php가 아니어도 상관없다) redirect 시키고 index.php에서 url을 분석해서 각각의 url에 맞는 기능으로 분기시키면 이제 php에서도 Java의 Controller 역할을 수행하는 것이 가능하게 된다.
아래는 index.php가  FrontController(혹은 Controller) 역할을 하도록 간단하게 코드의 뼈대만 작성하면 이런식이 될 것이다.

아래는 index.php가 FrontController(혹은 Controller) 역할을 하도록 간단하게 코드의 뼈대만 작성하면 이런식이 될 것이다.

$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);

switch($path)
{
case '/reserve/list' :
   //원하는 기능
   bareak;

case '/reserve/view' :
   //원하는 기능
   break;

case '/reserve/insert' :
   //원하는 기능
   bteak;

case '/reserve/list/json' :
   //원하는 기능
   bteak;

default :
   //에러 페이지
   break;
}

 

오라클에서 다음과 같은 상황에 대해 DB를 어떻게 변경하는지에 대해 포스팅하고자 한다.

상황 : 
-. 테이블을 새로 생성할수 있는 상황이 아닌 현재 이미 사용하고 있어 데이터가 적재되어 있는 테이블의 PK 변경 및 새로운 칼럼 추가하고자 하는 상황이다.
-. 현재 3개의 PK가 잡혀있는데 이 3개의 PK를 삭제하고 새로 추가하는 칼럼을 PK로 잡고자 한다.
-. PK로 잡히게 될 새로 추가하는 칼럼은 auto increment가 되는 칼럼이다.
-. 테이블 이름은 TB_MEMBER로 가정한다.
-. 새로운 PK로 사용하게 될 새롭게 생성하게 될 칼럼은 RSV_SEQ로 가정한다.

이상의 상황에서 다음 단계를 밟아 원하는 DB를 새롭게 구성할수 있다.

(1) 현재 사용하고 있는 3개의 PK를 삭제한다. TB_MEMBER라는 테이블에서 현재 사용하고 있는 PK들이 삭제된다. PK라는 속성이 제거되는것이지 해당 칼럼 자체가 삭제되는 것은 아니다.

ALTER TABLE TB_MEMBER DROP PRIMARY KEY; 

(2) 새로운 PK로 사용할 칼럼을 추가한다. 새로 추가될 칼럼인 RSV_SEQ를 추후 PK로 사용하게 될 것이고 이 칼럼은 auto increment되는 값을 갖게 될 칼럼이다. 그러나 지금의 시점에서는 단지 새로운 칼럼을 하나 추가하는 것 뿐이다. 

ALTER TABLE TB_MEMBER ADD(RSV_SEQ NUMBER); 

(3) PK로 사용하게 될 새로 추가할 칼럼의 검색 속도를 높이기 위해 index를 생성한다.

CREATE UNIQUE INDEX TB_MEMBER_PK ON TB_MEMBER(RSV_SEQ); 

(4) 새로 추가하게 될 PK 칼럼이 auto increment이므로 이에 대한 sequence를 생성한다. 오라클은 MySQL과 달리 해당 칼럼에 직접 auto_increment 속성을 지정할수가 없어서 별도의 시퀀서를 만들어 사용해야 한다. sequence의 이름은 TB_MEMBER_SEQ이고 1부터 1씩 자동 증가하는 방식의 sequence이다.

CREATE SEQUENCE TB_MEMBER_SEQ START WITH 1 INCREMENT BY 1; 

(5) 이상의 과정까지에서 PK로 사용하게 될 새로운 칼럼에는 아래와 같이 모두 null이 들어 있을 것이다.
---------------------------------
rsv_seq | aaa | bbb | ccc ...
---------------------------------
 null      | ...    | ...    | ...
 null      | ...    | ...    | ...
 null      | ...    | ...    | ...
---------------------------------
...
새로 생성한 칼럼인 RSV_SEQ에 대해 앞에서 생성한 sequence를 이용해서 auto increment된 값을 RSV_SEQ 칼럼에 update해 줘야 한다. 아래의 쿼리에 의해 기존 테이블의 각 row의 RSV_SEQ의 값은 1부터 시작해서 1씩 증가된 값들이 비로소 저장되게 된다.

UPDATE TB_MEMBER SET RSV_SEQ=TB_MEMBER_SEQ.nextval; 

---------------------------------
rsv_seq | aaa | bbb | ccc ...
---------------------------------
 1          | ...    | ...     | ...
 2         | ...    | ...     | ...
 3         | ...    | ...     | ...
---------------------------------

...

(6) 이제 비로소 새로 생성한 칼럼을 PK로 설정할수 있다. 아래와 같이 

ALTER TABLE TB_MEMBER ADD CONSTRAINT TB_MEMBER_PK PRIMARY KEY(RSV_SEQ); 

테스트 단계이거나 개발 단계에서 기존 적재되어 있는 데이터를 모두 날려 버리고 테이블을 새로 생성할수 있다면 굳이 위와 같은 다소 복잡한 과정 필요없이 그냥

drop table ...
create table ...로 간단히 변경된 내용이 적용된 DDL로 원하는 테이블을 구성할수 있을것이나 그러나 이미 사용하고 있는 경우나 기존 있는 데이터를 유지해야할 경우 오라클에서는 위와 같은 단계를 밟아서 처리를 해야 한다.

아래와 같은 Mapper xml의 쿼리 문이 있다고 할 경우,

<insert id="insertMember">
	insert into tbl_member (userid, userpw, username, email) values 
	(#{userid}, #{userpw}, #{username}, #{email})
</insert>

VO 객체는 다음과 같고,

public class MemberVO {
	private String userid;
	private String userpw;
	private String username;
	private String email;
	private Date regdate;
	private Date updatedate;

	... 이하 getter, setter는 생략 ...
}

DAO에서 Mapper xml에 있는 쿼리 실행을 다음과 같이 한다고 할때,

public void insertMember(MemberVO vo) {
	sqlSession.insert(namespace + ".insertMember", vo);
}

이럴때 sqlSession.insert(namespace + ".insertMember", vo)에서 vo 객체를 Mapper로 넘기면 #{userid}, #{userpw}, #{username}, #{email} 값들과 vo가 어떻게, 어떤 원리에 의해서 매핑이 되는가?

SqlSession 클래스의 insert() 메소드를 보면 다음과 같이 API 설명이 되어 있다.
int insert(String statement, Object parameter)
   ==>Execute an insert statement with the given parameter object. Any generated autoincrement 
   ==>values or selectKey entries will modify the given parameter object properties. 
   ==>Only the number of rows affected will be returned.
   Parameters:
     -. statement : Unique identifier matching the statement to execute.
     -. parameter : A parameter object to pass to the statement.
   Returns:
     -. int : The number of rows affected by the insert.

이 메소드의 첫 번째 매개인자인 statement는 이 statement의 문자열과 1:1로 대응되는 Mapper xml 파일에 있는 쿼리문을 지칭하게 된다.
이때 Mapper의 id값과 statement의 문자열이 동일한 쿼리문에 매핑된다.
위의 예에서는 Mapper xml 파일에서 id가 insertMember인 쿼리문을 실행하게 된다.

<insert id="insertMember">
	insert into tbl_member (userid, userpw, username, email) values 
	(#{userid}, #{userpw}, #{username}, #{email})
</insert>

두 번째 매개인자인 parameter는 첫 번째 매개인자인 statement가 가리키는 Mapper 클래스의 쿼리문을 실행할 때 이 쿼리문에서 사용할 변수들에(#{userid}, #{userpw}, #{username}...) 넘겨줄 값을 담고 있다. 
위이 예에서는 MemberVO 클래스의 객체인 vo가 되겠다.
이때 vo가 클래스가 가지고 있는 값들과 #{userid}, #{userpw}, #{username}, #{email} 이 변수들이 어떤 원칙에 의해서 연동되는가 하는 것이다.

이때 두 번째 매개인자인 parameter에는 다음과 같은 다양한 종류들이 가능한데

1) parameter가 하나이고 기본 자료형이나 문자열인 경우 값이 그대로 전달된다.

public MemberVO readMember(String userid) throws Exception {
	return (MemberVO)sqlSession.selectOne(namespace+".selectMember", userid);
}

Mapper xml에서는 userid와 동일한 mapper 변수와 1:1로 대응된다. 이 경우는 #{userid} = userid가 되는 것이다.

<select id="selectMember" resultType="org.zerock.domain.MemberVO">
	select 
	*
	from tbl_member
	where userid = #{userid}
</select>

2) parameter가 클래스의 객체인 경우 해당 클래스의 getter 메소드에 대응되서 mapper 변수가 값을 획득한다.
위의 vo 객체의 경우인 #{userid}, #{userpw}, #{username}, #{email}는 각각 vo 객체의 getUserid(), getUserpw(), getUsername(), getEmail()을 통해서 이들 각각의 Mapper 변수들의 값을 할당받게 된다.
만일 MemberVO 클래스의 멤버 변수에 dayCalc는 존재하지 않지만 MemberVO 클래스에 getDayCalc()이라는 getter가 만들어져 있다면 Mapper 변수에 #{dayCalc}과 같이 표현하면 정상적으로 값을 가져올 수 있게된다.

3) parameter가 클래스의 객체는 아니나 Mapper로 넘겨야 할 파라미터가 2개 이상일 경우 Map에 담아서 넘기는데 이 경우의 1:1 대응 원칙은 다음과 같다.

public MemberVO readWithPW(String userid, String userpw) throws Exception {
	Map<String, Object> paramMap = new HashMap<String, Object>();
	paramMap.put("userid",  userid);
	paramMap.put("userpw", userpw);
		
	return sqlSession.selectOne(namespace+".readWithPW", paramMap);
}

<select id="readWithPW" resultType="org.zerock.domain.MemberVO">
	select
		*
	from tbl_member
	where userid = #{userid} and userpw = #{userpw}
</select>

이 경우는 Map 객체의 key값과 Mapper의 변수가 1:1로 대응되서 값이 전달된다.
즉 Map 객체의 key 이름과 Mapper xml의 변수 이름이 동일해야 한다.

이상의 원리를 따라 MyBatis Mapper xml의 쿼리문과의 연동은 SqlSession 클래스가 알아서 매핑 작업을 처리해 준다.




MySQL db dump로 DB 내용을 백업 받은 것을 다시 MySQL에 복구하고자할 때 복구가 되지 않는 경우가 있다.
MySQL db에 접속할 계정의 user id가 root라고한다면 아래와 같은 커맨더가 정상적으로 DB를 복구해야 한다.
(test.sql이 DB 내용을 dump로 백업 받은 파일이고 복구하고자 하는 DB가 testDB라고 할 경우. 아래는 맥, 리눅스에서의 명령어 예이다)
 
$ mysql -uroot -p testDb < /Users/Documents/test.sql

testDb를 열어서 보면 Empty인 경우가 있다.
이렇게 복구가 안되면 다음을 체크해 볼것

아래의 내용은 커맨드 라인에서가 아닌 phpmyAdmin을 이용해서 복구를 할 경우에 대한 내용이다.
phpinfo()를 실행해서 나온 결과에서 

upload_max_filesize ==> 이 값이 아마도 기본 값 2M로 되어 있을 것이다.
post_max_size ==> 이 값이 아마도 기본 값 8M로 되어 있을 것이다.

이럴경우 test.sql의 크기가 upload_max_filesize에서 설정한 2Mb보다 클 경우 복구가 안될 것이다.
이 경우 php.ini 파일을 열어서 upload_max_filesize의 값을 더 높게 설정해 주어야 한다.

맥이나 리눅스의 경우 /etc/php.ini가 해당 파일의 경로이다.
참고적으로 post_max_size의 값이 upload_max_filesize의 설정 값보다 더 높게 설정해야 한다는 것이다.

자세한 정보는 아래를 참조

1.16 I cannot upload big dump files (memory, HTTP or timeout problems).

Starting with version 2.7.0, the import engine has been re–written and these problems should not occur. If possible, upgrade your phpMyAdmin to the latest version to take advantage of the new import features.

The first things to check (or ask your host provider to check) are the values of max_execution_time, upload_max_filesize, memory_limit and post_max_size in the php.ini configuration file. All of these settings limit the maximum size of data that can be submitted and handled by PHP. Please note that post_max_size needs to be larger than upload_max_filesize. There exist several workarounds if your upload is too big or your hosting provider is unwilling to change the settings:

Look at the $cfg['UploadDir'] feature. This allows one to upload a file to the server via scp, FTP, or your favorite file transfer method. PhpMyAdmin is then able to import the files from the temporary directory. More information is available in the Configuration of this document.

Using a utility (such as BigDump) to split the files before uploading. We cannot support this or any third party applications, but are aware of users having success with it.

If you have shell (command line) access, use MySQL to import the files directly. You can do this by issuing the “source” command from within MySQL:

source filename.sql;

+ Recent posts