2020년 3월 12일 목요일

Java DAO와 MyBatis Mapper xml의 쿼리문과의 매핑 원리

아래와 같은 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 클래스가 알아서 매핑 작업을 처리해 준다.

2020년 3월 11일 수요일

MySQL DB dump가 복구되지 않을때

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;

PHP에서 console.log()와 alert()으로 출력하기

PHP로 사이트를 개발하다보면 특정 변수의 값을 브라우저가 아닌 브라우저 콘솔에 출력해서 확인해 보고 싶은 때가 있다.
그런데 만일 PHP 소스에서 다음과 같이 하면

echo "<h1>Hello world</h1>";

이건 현재 웹 페이지에 Hello world라는 문자열을 큰 글씨로 출력하게 된다. 이 말인즉은 사용자가 특정 웹 페이지로 이동할때 갑자기 저 문구가 뜬금없이 큰 글씨로 사용자 웹 브라우저에 보여지게 된다는 것이다.
즉 echo로 출력하게 되면 브라우저 콘솔이 아닌 브라우저 화면 자체에 출력이 된다.
그러면 일반 사용자에게는 보이지 않고 콘솔에 출력할려면 어떻게 해야 되는가?
다음과 같이 하면 된다.

echo '<script>';
echo 'console.log(“Hello world”)’;
echo '</script>';

그러면 이번에는 특정변수의 값을 출력할려면 어떻게 해야 하는가? 아래와 같이...

echo '<script>';
echo 'console.log("'.$sql_notice.'")';
echo '</script>';

그러면 이번에는 콘솔이 아닌 alert()을 이용해서 화면에 출력할려면? 아래와 같이...

echo '<script>';
echo 'alert("Yes, Mobile~");';
echo '</script>';

그러면 alert()에 특정 변수의 값을 출력할려면? 아래와 같이..

echo '<script>';
echo 'alert("isMobile : '.$isMobile .'");';
echo '</script>';

그러면 이번에는 콘솔에 특정 변수 하나의 값이 아닌 배열의 내용을 출력할려면?
다른 곳에서 퍼온 내용인데 아주 유용하다. 예를 들어서 DB에서 가져온 배열의 내용이 $result에 담겨 있을 때 이를 출력할려면 print_r($result)로 하면 화면 상에서 쉽게 확인이 되지만 실제 운영중인 사이트의 경우는 참으로 곤란해 진다. 이때 아래와 같이 하면 콘솔 상에서 배열에 담긴 많은 내용을 사용자 화면에 아무런 영향을 주지 않고 콘솔 상에서 쉽게 확인이 가능하다.

$result에 DB에서 가져온 값이 배열로 담겨 있다고 할때, 아래와 같이

//$result에 담긴 배열 값 콘솔에 출력하기
echo "<script>\r\n//<![CDATA[\r\nif(!console){var console={log:function(){}}}";
$arr = explode("\n", print_r($result, true));

foreach ($arr as $temp) {
  if (trim($temp)) {
      $temp = addslashes($temp);
      echo "console.log(\"{$temp}\");";
  }
}
echo "\r\n//]]>\r\n</script>";

위의 경우는 배열의 key-value중 value 값만 출력한 경우라면 이번에는 배열의 key-value 형태로 출력하는 경우를 보자.

//배열을 console에 출력하기($result에 배열 형태의 데이터가 있을 때 key-value 형태로 출력하기)
echo "<script>\r\n//<![CDATA[\r\nif(!console){var console={log:function(){}}}";
foreach ($result as $key => $line) {
        $key = addslashes($key);
        $line = addslashes($line);
        echo "console.log(\"${key} : {$line}\");";
}
echo "\r\n//]]>\r\n</script>";