edu day 29, 30
23/06/23 - MyBatis 개념, 환경, 코드
23/06/26 - Exception, List에 Map담기
23/06/27 - 주의 문법
앞서 배운 statememt, preparedstatement 는 oracle과 연동하기 위해 Connection, Statement, ResultSet 등을 사용하면서 코드를 구현하였다.
그러나 이러한 코드는 변수 또는 중복이 많이 생겨서 추상화해도 깔끔하지 않은 코드로 보인다.
더 깔끔하고 나은 방법으로 구현 하기 위해 MyBatis를 사용하게 되는 것이다.😁
MyBatis는 객체 지향 언어의 자바의 JDBC를 이용한 퍼시스턴스 프레임워크이다.
다만 단점이 있다면, 맨 처음 환경을 구현하는 것이 복잡하다. 처음만 제대로 구현해 놓으면 가독성 좋은 메서드를 활용하여 깔끔하고 간결하게 코드를 구현할 수 있다.
- JDBC를 이용한 커넥션 코드 및 변수 등 중복 작업을 대체해준다.
- SQL, 동적 쿼리, 저장 프로시저 그리고 고급 매핑을 지원하는 SQL Mapper이다.
- SQL 쿼리들을 따로 XML파일로 작성하여 프로그램 코드를 간결화 하기 용이하다.
- DAO에서 호출한 id와 mapper에서 설정된 id가 다를 경우
- mapper에서 설정한 id 중 중복된 id가 있을 경우
#key = value
oracle.jdbc=oracle.jdbc.driver.OracleDriver
oracle.url=jdbc:oracle:thin:@localhost:1521:xe
oracle.userid=scott
oracle.passwd=tiger
mysql.jdbc=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql:localhost:3306\orcl
mysql.userid=scott
mysql.passwd=tigerc
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- jdbc.properties파일 등록 -->
<properties resource="jdbc.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!-- db 연결 4가지 정보 -->
<dataSource type="POOLED">
<property name="driver" value="${oracle.jdbc}"/>
<property name="url" value="${oracle.url}"/>
<property name="username" value="${oracle.userid}"/>
<property name="password" value="${oracle.passwd}"/>
<!-- <property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
<property name="username" value="scott"/>
<property name="password" value="tiger"/>-->
</dataSource>
</environment>
</environments>
<!-- Mapper.xml의 경로 등록 (Mapper.xml파일이 존재해야함 ) -->
<mappers>
<mapper resource = "DeptMapper.xml"/>
</mappers>
</configuration>
<?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="org.mybatis.example.BlogMapper">
<select id="selectAll" resultType="com.dto.Dept">
select * from dept
</select>
<insert id="deptInsert" parameterType="com.dto.Dept">
insert into dept values(#{deptno}, #{dname}, #{loc})
</insert>
<update id="deptUpdate" parameterType="com.dto.Dept">
update dept set dname=#{dname}, loc=#{loc} where deptno=#{deptno}
</update>
<delete id="deptDelete" parameterType="int">
delete from dept where deptno=#{i}
</delete>
<select id="selectByDeptno" parameterType="int" resultType="com.dto.Dept">
select deptno, dname, loc from dept where deptno=#{deptno}
</select>
</mapper>
package com.dto;
public class Dept {
int deptno;
String dname;
String loc;
public Dept() {
super();
// TODO Auto-generated constructor stub
}
public Dept(int deptno, String dname, String loc) {
super();
this.deptno = deptno;
this.dname = dname;
this.loc = loc;
}
@Override
public String toString() {
return "Dept [deptno=" + deptno + ", dname=" + dname + ", loc=" + loc + "]";
}
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
}
package com.config;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MySqlSessionFactory {
static SqlSessionFactory sqlSessionFactory= null;
static {//초기화
String resource = "Configuration.xml"; //수정이 필요한 유일한 부분
//Configuration.xml의 경로가 바뀔시에 수정이 필요
InputStream inputStream= null;
try {
inputStream = Resources.getResourceAsStream(resource);
System.out.println("configuration.xml 로딩 성공 ");
} catch (IOException e)
{
System.out.println("configuration.xml 로딩성공ㄴ");
e.printStackTrace();
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//DriverManager와 비슷한 객체
}//end static
//SqlSession 반환
public static SqlSession getSqlSession() {//
// SqlSession session= MySqlSessionFactory.getSqlSession();
SqlSession session = sqlSessionFactory.openSession();
//getConnection의 Connection과 비슷한 기능의 SqlSession객체 생성 리턴
return session;
}
}
📌DeptMapper.xml 에서 mapper로 설정해준 select, insert, update, delete 들은 제외하고, 여기까지 코드는 기본으로 구현해야 Oracle과 연동을 할 수 있다.
앞서 배운 Connection대신, 위 코드에서 만든 메서드 SqlSession을 이용하여 로딩한다.
import java.util.List;
import com.dto.Dept;
import com.service.OracleMyBatisService;
public class OralceMyBatisMain {
public static void main(String[] args) {
OracleMyBatisService service = new OracleMyBatisService();
service.insert(new Dept(99, "개발", "강남"));
service.update(new Dept(99, "영업", "서울"));
service.delete(99);
Dept dept = service.selectByDeptno(99);
System.out.println(dept);
//selectAll
List<Dept> list = service.selectAll();
for (Dept d: list) {
System.out.println(d);
}
}
}
package com.service;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.config.MySqlSessionFactory;
import com.dao.OracleMyBatisDAO;
import com.dto.Dept;
public class OracleMyBatisService {
OracleMyBatisDAO dao;
public OracleMyBatisService() {
super();
// TODO Auto-generated constructor stub
dao = new OracleMyBatisDAO();
}
public List<Dept> selectAll() {
List<Dept> list = null;
SqlSession session = MySqlSessionFactory.getSqlSession();
list = dao.selectAll(session);
return list;
}
public void insert(Dept dept) {
// TODO Auto-generated method stub
SqlSession session = MySqlSessionFactory.getSqlSession();
try {
dao.insert(session, dept);
session.commit();
} finally {
session.close();
}
}
public void update(Dept dept) {
// TODO Auto-generated method stub
SqlSession session = MySqlSessionFactory.getSqlSession();
try {
dao.update(session, dept);
session.commit();
} finally {
session.close();
}
}
public void delete(int i) {
SqlSession session = MySqlSessionFactory.getSqlSession();
try {
dao.delete(session, i);
session.commit();
} finally {
session.close();
}
}
public Dept selectByDeptno(int i) {
SqlSession session = MySqlSessionFactory.getSqlSession();
Dept dept = dao.selectByDeptno(session, i);
return dept;
}
}
package com.dao;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.dto.Dept;
public class OracleMyBatisDAO {
public List<Dept> selectAll(SqlSession session) {
List<Dept> list = session.selectList("selectAll");
return list;
}
public void insert(SqlSession session, Dept dept) {
session.insert("deptInsert", dept);
}
public void update(SqlSession session, Dept dept) {
session.update("deptUpdate", dept);
}
public void delete(SqlSession session, int i) {
session.delete("deptDelete", i);
}
public Dept selectByDeptno(SqlSession session, int deptno) {
Dept dept = session.selectOne("selectByDeptno",deptno);
return dept;
}
}
Mapper.xml파일 내부의<mapper> </mapper>태그 사이에 사용할 SQL문을 작성한다.- 사용할 DQL, DML을
<select> </select>형식으로 작성한다.- 만약 사용자가 지정하고 싶은
SELECT문이 여러 개 라면, 분류하기 위해 sql마다id를 지정해주고resultType과parameterType를 지정해준다. 그 후<select> </select>태그 내부에 sql문을 작성해준다.parameterType으로 값을 가져온 변수 또는 클래스는#{변수}형식으로 받아준다.
이 변수를 받는 이름은 SqlSession 객체의 메서드를 선언한 함수에서 지정한 변수의 이름이다.- ❗주의할 점은 sql문을 작성할 때
;(세미콜론)은 제외시킨다.
📌형식 1
<select id="selectByDeptno" parameterType="int" resultType="com.dto.Dept">
select deptno, dname, loc from dept where deptno=#{deptno}
</select>
📌형식 2<update id="deptUpdate" parameterType="com.dto.Dept">
update dept set dname=#{dname}, loc=#{loc} where deptno=#{deptno}
</update>
❗앞서 설명한 Mapper의 id와 호출한 메서드에 넣은 id를 다르게 작성하여 Exception상황이 발생하지 않게 주의하자!!
configuration.xml에 매퍼 지정이 제대로 안되었거나 DAO, Mapper에서 이름이 틀렸을 때, 쿼리문 id가 일치하지 않을 때 등 Exception 발생
Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.dept.DeptMapper2.selectAll ### Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for com.dept.DeptMapper2.selectAll
query문 실행 코드에 오류가 있을 때
Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: ORA-00936: missing expression
DeptMapper.xml의 쿼리문에서 resultType, parameterType 등에 오류가 있을 때
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.service.OracleMyBatisService.selectAll(OracleMyBatisService.java:22)
at OracleMyBatisMain3.main(OracleMyBatisMain3.java:11)
Caused by: org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in DeptMapper2.xml
반환 되는 값 또는 출력하는 값이 null값인 경우 NullPointerException 발생.
&& configuration.xml의 DB연결 정보(4가지)가 맞지 않을 때도 널포인터예외 발생.
&& DeptMapper.xml의 쿼리문 실행 코드와 parameterType이 일치하지 않을 경우도 ❗NullPointerException이 발생한다.
Exception in thread "main" java.lang.NullPointerException : Cannot invoke "java.util.List.iterator()" because "list" is null
at OracleMyBatisMain3_1.main(OracleMyBatisMain3_1.java:17)
<HashMap> 형식의 데이터 담기import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import com.service.OracleMyBatisService;
public class OracleMyBatisMain4_1 {
public static void main(String[] args) {
OracleMyBatisService service = new OracleMyBatisService();
List<HashMap> list = service.selectAllHashmap();
for (int i = 0; i < list.size(); i++) {
String dname = (String)list.get(i).get("DNAME");
String loc = (String)list.get(i).get("LOC");
int deptno = ((BigDecimal)list.get(i).get("DEPTNO")).intValue();
System.out.println(deptno +"\t" + dname + "\t" + loc);
}
for (HashMap Map : list) {
Set <String> keys = Map.keySet();
for (String key : keys) {
System.out.print(Map.get(key) + "\t");
}
System.out.println();
}
}
}
package com.service;
import java.util.HashMap;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.config.MySqlSessionFactory;
import com.dao.OracleMyBatisDAO;
import com.dto.Dept;
public class OracleMyBatisService {
OracleMyBatisDAO dao;
public OracleMyBatisService() {
super();
dao = new OracleMyBatisDAO();
}
public List<HashMap> selectAllHashmap() {
SqlSession session = MySqlSessionFactory.getSqlSession();
List<HashMap> list = null;
try {
list = dao.selectAllHashmap(session);
} finally {
session.close();
}
return list;
}
}
package com.dao;
import java.util.HashMap;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.dto.Dept;
public class OracleMyBatisDAO {
public List<HashMap> selectAllHashmap(SqlSession session) {
List<HashMap> list = session.selectList("selectAllHashmap");
return list;
}
<select id="selectAllHashmap" resultType="hashmap">
select deptno, dname, loc from dept order by 1
</select>
<foreach> </foreach>item이 하나의 인자고 collection이 받은 전체의 인자이다.open과 close로 시작과 끝을 나타내고 구분자 separator를 지정해 인자를 구분지어 sql문을 완성할 수 있다. <update id="multiUpdate" parameterType="arraylist">
update dept set loc ='제주' where deptno in
<foreach item="item" collection="list" open="(" separator="," close=")">
#{item}
</foreach>
</update>
<foreach> 내부에 클래스 객체 전체를 받는 item에서 클래스에서 선언한 멤버변수의 이름으로 접근하면 된다. <delete id="multiDelete" parameterType="arraylist">
delete from dept where deptno in
<foreach item="item" collection="list" open="(" separator="," close=")">
#{item.deptno}
</foreach>
</delete>
그래서 부등호는 <![CDATA[>]> 태그를 사용하거나 < 또는 >를 사용하여 부등호를 작성한다.
<select id="selectAll2" resultType="com.dto.Dept">
select * from dept where deptno < 10
</select>
<select id="selectAll3" resultType="com.dto.Dept">
select * from dept where deptno <![CDATA[>]]> 10
</select>