23.02.13

이동욱·2023년 8월 6일
0

NaverCloudCamp

목록 보기
24/42

학습내용

  1. ibatis/mybatis
  2. Log framework
  3. log framework(log4j)
  4. jdbc/mybatis null 입력
  5. jdbc/mybatis null 조회

ibatis/mybatis

기존 JDBC는 Connetcion -> Statement -> ResultSet, int 절차를 따른다.
위 절차를 따를시 새로운 Persistence Layer Logic을 작성할때마다 매번 같은 source code를 동일하게 넣어야한다.

//기존 JDBC 절차
public class JDBCTestApp {
	public static void main(String[] args) {
		String dburl = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
		String dbuser ="ibatis";
		String dbpwd = "ibatis";

		Connection con = null;
		PreparedStatement pStmt = null;
		ResultSet selectResult = null;
		
		List<User> list = null; 
		try{		
			Class.forName("oracle.jdbc.driver.OracleDriver");
			con = DriverManager.getConnection(dburl,dbuser,dbpwd);
			
			StringBuffer  userSelectAll = new StringBuffer();
			userSelectAll.append("SELECT ");
			userSelectAll.append("user_id, user_name, password, age, grade, reg_date ");
			userSelectAll.append("FROM USERS ");
			pStmt = con.prepareStatement( userSelectAll.toString() );

			selectResult = pStmt.executeQuery();

			list = new ArrayList<User>();			
				User user = new User();
				 user.setUserId( selectResult.getString("user_id") );
				 user.setUserName( selectResult.getString("user_name") );
				 user.setPassword( selectResult.getString("password") );
				 user.setAge( selectResult.getInt("age") );
				 user.setGrade( selectResult.getInt("grade") );
				 user.setRegDate( selectResult.getTimestamp("reg_date"));
				 list.add( user );
			 }		
		}catch(ClassNotFoundException e){		
			throw new RuntimeException(e.getMessage(), e);
		}catch(SQLException e){		
			throw new RuntimeException(e.getMessage(), e);
		}finally{
			if(selectResult != null){
				try{	
					selectResult.close();		
				}catch(Exception e1){  }
			}
			if(pStmt != null){
				try{	
					pStmt.close();	
				}catch(Exception e2){  }
			}
			if(con != null){
				try{	
					con.close();		
				}catch(Exception e3){  }
			}
		}
	}
}

mybatis framework를 통해 이러한 copy & paste 문제를 해결 할 수 있다.

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>

  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>    
      <dataSource type="POOLED">
        <property name="driver"  value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:xe"/>
        <property name="username"  value="ibatis"/>
        <property name="password" value="ibatis"/>
      </dataSource>
    </environment> 
  </environments>

  <mappers>
    <mapper resource="sql/UserMapper.xml"/>
  </mappers>

</configuration>

userMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
		PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
		"http://mybatis.org/dtd/mybatis-3-mapper.dtd">	
 					
<mapper namespace="UserMapper">
  	<select 	id="getUserList" 	resultType="mybatis.service.domain.User">
		SELECT
		user_id 			AS userId,
		user_name 	AS userName,
		password 		AS password,
		age 					AS age,
		grade				AS grade,
		reg_date 		AS regDate
		FROM users   											
  	</select>	
</mapper>

MybatisTestApp

public class MyBatisTestApp {
	public static void main(String[] args) throws Exception{
		Reader reader = Resources.getResourceAsReader("sql/mybatis-config.xml");
		

		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		SqlSession sqlSession = sqlSessionFactory.openSession();

		List<User> list = sqlSession.selectList("UserMapper.getUserList");
		
		sqlSession.close();

		for (User user : list) {
			System.out.println( user.toString() ) ;
		}
	}
}

XML을 parsing하기에 byte stream이 아닌 문자열 단위로 읽어들이는 Reader를 파라미터로 넣어주었다

log framework(log4j)

개발중 에러를 잡기위해 sysout으로 콘솔에 로그를 출력 할 수 있다.
그러나 개발이 완료되어 더 이상 로그가 필요 없다면 일일히 모든 로그를 지워야하고
다시 새로운 기능을 추가 할 때 로그를 다시 작성하고 지워야하는 번거로움이 발생한다.
이러한 문제영역을 해결하기 위해 log framework를 사용 할 수 있다.
log4j.properties

#에러를 콘솔에 출력한다, 출력 정보를 DEBUG, ERROR 옵션등으로 조절 할 수 있다.
log4j.rootLogger=ERROR, console

#콘솔에 에러를 출력하기 위한 class
log4j.appender.console=org.apache.log4j.ConsoleAppender

log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%-5p [%t] - %m%n

jdbc/mybatis null 입력

기존 jdbc null 입력

public class JDBCTestApp04 {
	public static void main(String[] args) throws Exception{
		String dburl = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
		String dbuser ="ibatis";
		String dbpwd = "ibatis";

		Connection con = null;
		PreparedStatement pStmt = null;
		
		User user = new User("user04","주몽","user04",null,0);
		
		Class.forName("oracle.jdbc.driver.OracleDriver");
		con = DriverManager.getConnection(dburl,dbuser,dbpwd);

		StringBuffer  insertSQL = new StringBuffer();
		insertSQL.append("INSERT ");
		insertSQL.append("INTO USERS( user_id,user_name,password,age,grade,reg_date) ");
		insertSQL.append("VALUES(?,?,?,?,?,?) ");
				

		pStmt = con.prepareStatement( insertSQL.toString() );
		pStmt.setString(1, user.getUserId());
		pStmt.setString(2, user.getUserName());
		pStmt.setString(3, user.getPassword());
		pStmt.setNull(4,Types.INTEGER);	  
		pStmt.setNull(5,Types.NUMERIC); 	
		pStmt.setNull(6, Types.DATE);	
		
		int isInsert = pStmt.executeUpdate();

		System.out.println( "SQL :: "+ insertSQL.toString() );
		System.out.println("INSERT 유무 : "+isInsert);
		
		 pStmt.close();		 con.close();
	}
}

jdbc에서는 prepareStatement의 set~()을 통해 파라미터로 넘겨줄 자료형을 명시해준다.
reference type들은 set~()을 통해 null의 값을 넣어줄 수 있지만 primitive type들은
pStmt.setInt(null);처럼 null을 입력하면 오류가 발생한다. 따라서 setNull()메서드를 통해
null값을 넣어주고 파라미터로 값을 넣어주는 칼럼의 데이터타입을 명시한다.

mybatis null 입력

	<insert id="addUser01"  parameterType="user">
  		INSERT 
  		INTO users(user_id, user_name, password, age, grade, reg_date)
  		VALUES (#{userId, jdbcType=VARCHAR}, 
  				#{userName, jdbcType=VARCHAR}, 
  				#{password, jdbcType=VARCHAR}, 
  				#{age, jdbcType=INTEGER}, 
  				#{grade, jdbcType=NUMERIC}, 
  				SYSDATE)
  	</insert>
  	
  	<insert id="addUser02"  parameterType="user">
  		INSERT 
  		INTO users(user_id, user_name, password, age, grade, reg_date)
  		VALUES (#{userId : VARCHAR}, 
  				#{userName : VARCHAR}, 
  				#{password : VARCHAR}, 
  				#{age : INTEGER}, 
  				#{grade : NUMERIC}, 
  				SYSDATE)
  	</insert>

mybatis는 xml metaData를 사용해 mapping을 해주면 파라미터의 null값을 처리해준다.

jdbc/mybatis null 조회

jdbc null data 조회

public class JDBCTestApp05 {
	public static void main(String[] args) throws Exception{
		String dburl = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
		String dbuser ="ibatis";
		String dbpwd = "ibatis";

		Connection con = null;
		PreparedStatement pStmt = null;
		ResultSet rs = null;
		
		User user = new User("user04","주몽","user04",null,0);
		
		Class.forName("oracle.jdbc.driver.OracleDriver");
		con = DriverManager.getConnection(dburl,dbuser,dbpwd);
		
		StringBuffer  insertSQL = new StringBuffer();
		insertSQL.append("INSERT ");
		insertSQL.append("INTO USERS( user_id,user_name,password,age,grade,reg_date) ");
		insertSQL.append("VALUES(?,?,?,?,?,?) ");

		StringBuffer  selectSQL = new StringBuffer();
		selectSQL.append("SELECT ");
		selectSQL.append("user_id, user_name, password, age, grade, reg_date ");
		selectSQL.append("FROM USERS WHERE user_id=?");
		
		pStmt = con.prepareStatement( insertSQL.toString() );
		pStmt.setString(1, user.getUserId());
		pStmt.setString(2, user.getUserName());
		pStmt.setString(3, user.getPassword());
		pStmt.setNull(4,Types.INTEGER);	  
		pStmt.setNull(5,Types.NUMERIC); 	
		pStmt.setNull(6, Types.DATE);		
		
		int isInsert = pStmt.executeUpdate();

		User vo = new User();
		
		if( rs.next() ){
			 vo.setUserId( rs.getString("user_id") );
			 vo.setUserName( rs.getString("user_name") );
			 vo.setPassword( rs.getString("password") );
			 rs.getObject("password");
			 vo.setAge( rs.getObject("age") );
			 vo.setGrade( rs.getObject("grade") );
			 vo.setRegDate( rs.getObject("reg_date"));
		 }
		rs.close();		 
		pStmt.close();		 
		con.close();
	}
}

레코드가 null인 컬럼을 primitive field에 넣어준다면 에러가 발생한다
따라서 jdbc에서는 자료형에 따른 ResultSet 의 get~()메서드를 제공하여 DB의 null 레코드를 가져온다
rs.getInt("age")에서 resultSet의 컬럼 age의 레코드가 null이면 int에 맞는 0으로 변환하고
rs.getObject("age")에서는 Object로 반환하기에 null을 반환한다.

mybatis null data 조회

	<!-- mapper.xml -->
  	<resultMap type="user" id="userSelectMap">
  		<result property="userId"  		column="user_id" 	jdbcType="VARCHAR"/>
  		<result property="userName" 	column="user_id" 	jdbcType="VARCHAR"/>
  		<result property="password"  	column="password" 	jdbcType="VARCHAR"/>
  		<result property="age"  		column="age" 		jdbcType="INTEGER"/>
  		<result property="grade"  		column="grade" 		jdbcType="NUMERIC"/>
  	</resultMap>
	
	<select id="getUserList" resultMap="userSelectMap">
  		SELECT user_id, user_name, password, age, grade, reg_date
		FROM users 
  	</select>

mybatis는 alias를 통해 DB의 snake case를 camel case로 변경하지 않아도 된다는점과 가독성이 더 좋다는점에서
resultType보다는 resultMap을 더 많이 사용한다
resultMap은 db에서 조회한 데이터를 domain객체로 binding 할때 db의 데이터가 null인 경우
setter metod를 호출하지 않기에 reference type field이면 null, primitive type field이면 0으로 초기화된다.

mybatis에서 select 결과를 resultMap으로 받아올때 레코드가 null이면 setter 메서드를 호출하지 않는다.
이때 위 옵션을 통해 레코드가 null이더라고 setter메서드를 호출 할 수 있다.

profile
Backend Developer

0개의 댓글