본문 바로가기

개발/Spring

Spring 2장 - MyBatis Framework

반응형

MyBatis

Java의 관계형 데이터베이스 프로그래밍을 좀 더 쉽게 할 수 있는 개발 프레임워크
XML 또는 Annotation을 이용해 저장 프로시저나 SQL문을 객체와 연계하는 체계
SQL Mapper로 SQL 문장과 자바 객체를 매핑
JDCB 코드와 수동 설정의 파라미터를 작성할 필요가 없다

MyBatis 특징

- 파라미터로써 객체를 제공
파라미터 객체는 JavaBeans, Map 또는 원시타입 래퍼
파라미터 객체는 update 문 내에 입력 값을 세팅하기 위해 사용되거나 쿼리문의 where 절을 세팅하기 위해 사용됨

- 매핑된 statement를 실행
Sql Map 프레임워크는 PreparedStatement 인스턴스를 생성하고 제공된 파라미터 객체를 사용해서 파리미터를 설정
Statement를 실행하고 ResultSet으로부터 결과 객체를 생성함

- 결과 값을 반환함
update의 경우 영향을 미친 rows 숫자를 반환함
select의 경우 한 개의 객체 또는 컬렉션 객체를 반환
결과 객체는 JavaBeans, Map, 원시타입래퍼 또는 XML이 될 수 있음

- pom.xml의 <dependencies> 영역에 추가하거나 프로젝트 생성 시 미리 추가할 수 있다

# mybatis config, mapper
mybatis.config-location: classpath:mybatis-config.xml
// mapper xml 파일들의 경로를 지정한다. 
// mapper/**/*.xml는 mapper 디렉토리 아래에 있는 모든 .xml을 의미한다.
// mapper 인터페이스와 매퍼 xml 파일 간의 매핑 정보를 설정하는데 사용한다.
mybatis.mapper-location-locations=mapper/**/*.xml

 

Mapper

Java 객체를 XML Map을 이용해 SQL문 또는 저장 프로시저와 연결해주는 Data Mapper
매핑된 SQL 구문을 작성하고 여러 map 파일을 참조할 수 있음

 

SELECT 문

<select id="selectAllTest" resultType="Test">
    SELECT no, name, createdtime, updatedtime FROM testtable 
</select>
<select id="selectTest" parameterType="Test" resultType="Test">
    SELECT no, name, createdtime, updatedtime FROM testtable where no=#{no} 
</select>
id
SQL문을 찾기 위한 고유 식별자, namespace와 결합하여 사용
parameterType 
SQL문에 전달될 클래스명이나 Alias
resultType
SQL문이 리턴하는 리턴 타입 지정
resultMap
SQL 문의 실행 결과 컬럼명과 Java Object의 필드명이 다를 때 사용할 맵 정보
Java 객체와 Select 문의 실행 결과를 매핑
한번 정의된 ResultMap은 여러 Sql문에서 사용 가능

 

UPDATE, DELETE, INSERT 문

<update id="update" parameterType="Test">
    UPDATE test SET name=#{name}, updatedtime=#{updatedtime} WHERE no=#{no}
</update>

<delete id="delete" parameterType="Test">
    DELETE test WHERE no=#{no}
</delete>

<insert id="insert" parameterType="Test" useGeneratedKeys="true" keyProperty="no">
    INSERT INTO test(name, createdtime, updatedtime) VALUES (#{name}, #{createdtime}, #{updatedtime})
</insert>

<insert id="insert2" parameterType="Test">
    // contact_seq 이름의 시퀀스의 다음 값을 받아내고 이 값을 no 속성에 할당
    <selectKey keyProperty="no" resultType="int" order="BEFORE">
        select contact_seq.nextval from dual
    </selectKey>
    // 위 구문이 실행된 Test 객체를 이용해 아래 문 실행
    INSERT INTO test (no, name, createdtime, updatedtime) VALUES (#{no}, #{name}, #{createdtime}, #{updatedtime})
</insert>
ParameterType
데이터 추가를 위해 전달하는 자바 객체
useGeneratedKeys 
데이터베이스 내부에서 생성한 키 (예: 자동증가 필드)를 받는 JDBC getGeneratedKeys 메서드를 사용하도록 설정
keyProperty
userGeneratedKeys 에 의해 리턴된 키를 설정할 속성을 지정한다
selectKey
selectKey 태그 내의 구문이 먼저 실행된다

 

sql 사용

재사용 가능한 SQL 구문을 정의할 때 사용한다.
<sql> 태그를 사용해 코드를 작성하면 MyBatis에서 동적 SQL을 생성하고 관리한다.
이 코드 스니펫은 MyBatis의 매퍼 XML 파일 내에서 사용되며, 동적 SQL 조각을 정의하고 재사용하는 데 사용한다.

// sql 조각 정의
<sql id="select-person">
    select * from person
</sql>

// sql 조각 정의
<sql id="person-id">
    // #{id} 는 파라미터 바인딩을 하여 id 파라미터 값을 쿼리에 삽입한다
    where id = #{id}
</sql>

// sql 쿼리 실행
<select id="getPerson" resultType="Person">
    // sql 조각 호출
    <include refid="select-person" />
    <include refid="person-id" />
</select>

 

// sql 조각
<sql id="personColumns"> 
    // #{alias} 는 테이블 별칭을 나타내며 실제 sql 쿼리가 실행될 때 실제 테이블 이름으로 대체됨
    #{alias}.id, #{alias}.username, #{alias}.password
</sql>

<select id="selectPersonList" resultType="map">
    SELECT
        // sql 조각 호출
        <include refid="personColumns"> 
             // alias 파라미터 값을 t1로 설정
             <property name="alias" value="t1"/>
        </include>
        // sql 조각 호출
        <include refid="personColumns"> 
             // alias 파라미터 값을 t2로 설정
             <property name="alias" value="t2"/>
        </include>
    FROM PersonTable t1
    CROSS JOIN PersonTable t2
</select>
${alias} 
문자열 치환 방법
실제 SQL 쿼리가 실행될 때 실제 테이블 이름 또는 컬럼 이름으로 대체됨
메타데이터(ex. 테이블 명 or 컬럼 명)가 동적으로 변경될 때 유용
그러나 MyBatis가 직접 문자열을 SQL문장에 삽입하기에 사용자 입력을 직접 SQL 문장에 삽입하는 경우 SQL 인젝션 공격이 일어날 수 있어 사용자 입력 값을 바로 삽입 시 주의 필요

#{alias}
파라미터 바인딩 방법
MyBatis 가 이 방식을 사용해 PreparedStatement 속성을 생성하고 값을 안전하게 PreparedStatement 파라미터에 설정
안전하고 빠르게 전달할 수 있기 때문에 #를 사용할 수 있다면 $를 사용하지 않는 게 좋음

PreparedStatement
Java JDBC (Java Database Connectivity) 에서 SQL 쿼리를 실행하기 위해 사용하는 인터페이스
Statement 인터페이스의 하위 인터페이스로 매개변수화된 쿼리를 실행할 때 사용
매개변수화된 쿼리는 쿼리 문자열 내에 물음표(?)를 사용하여 매개변수를 표시하고, 이 값에 실제 값을 설정하여 쿼리를 실행
PreparedStatement를 사용하면 쿼리가 한 번 컴파일되고, 이후는 매개변수 값만 변경하여 동일한 쿼리를 여러 번 실행 가능
즉 쿼리의 성능을 향상시키고 코드 재사용성을 높임

 

파라미터 전달

파라미터 값으로 원시 변수 전달할 시, parameterType을 제외하고 전송할 수 있다.

<select id="getPerson" resultType="Person">
    SELECT no, name, createdtime, updatedtime FROM person
    WHERE no=#{no}
</select>

파라미터 값으로 객체나 클래스 등 원시 변수가 아닌 것을 전달할 시, 자바 객체나 맵을 이용한다.

<update id="updatePerson" parameterType="Person">
    UPDATE person
    SET name=#{name}, updatedTime=#{updatedTime}
    WHERE no=#{no}
</update>

 

 

SQL Session

SqlSession Bean

MyBatis는 SqlSessionFactory를 사용해 SqlSession 인스턴스를 생성하고 이를 통해 SQL 쿼리를 실행한다.
MyBatis는 트랜잭션 관리를 수동으로 해야하나 MyBatis-Spring은 트랜잭션 관리가 자동화되어 있다.

트랜잭션 수동 관리
SqlSession을 열고, 쿼리를 실행한 후, 트랜잭션을 Commit하거나 Rollback하고, 마지막에 SqlSession을 닫는 작업

SqlSessionFactoryBean을 사용해 SqlSessionFactory를 설정하는 방법이다.

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:/mybatis-config.xml"></property>
    <property name="mapperLocations" value="classpath:/mappers/**/*.xml" />
</bean>
// DAO Class에 SqlSession 객체를 주입시켜 SQL Map 실행
// Spring 설정 파일에서 설정 (아래 예시)
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplete">
    <constructor-arg index="0" ref"sqlSessionFactory">
</bean>
constructor-arg
SqlSessionTemplete 빈에 의존성 주입을 설정
index
생성자의 특정 인자의 인덱스를 지정
생성자에 여러 인자가 있을 때 어떤 인자에 값을 전달할지 명확하게 하는 데 사용
ref
SqlSessionTemplete 생성자가 sqlSessionFactory 빈의 인스턴스를 받겠다는 의미

 

SqlSession 데이터 접근 메소드

1. SelectOne 단일 조회

String statement = "com.example.mapper.UserMapper.selectById";
Integer userId = 1;
User user = sqlSession.selectOne(statement, userId);

결과가 여러개라도 첫번째 레코드만 반환하고 나머지는 무시한다.
여러 레코드를 반환하는 쿼리를 실행하려고 해도 MyBatis는 예외를 발생시키지 않고 단일 레코드만 반환한다.

2. SelectList 목록 조회

String statement = "com.example.mapper.UserMapper.selectAllUsers";
List<User> users = sqlSession.selectList(statement);

결과를 리스트로 반환한다.

3. insert

String statement = "com.example.mapper.UserMapper.insertUser";
User newUser = new User("John", "Doe");
int affectedRows = sqlSession.insert(statement, newUser);

 

신규 데이터를 삽입한다.
작업이 실행되면 생성된 Row의 키 값을 반환한다.
MyBatis는 중복을 허용하지 않기 때문에 이미 존재하던 데이터인 경우 insert 작업은 무시된다.

4. update

String statement = "com.example.mapper.UserMapper.updateUser";
User updatedUser = new User(1, "Jane", "Doe");
int affectedRows = sqlSession.update(statement, updatedUser);

기존 데이터를 수정한다.
작업이 실행되면 적용된 Row의 개수를 반환한다.
update는 기존 레코드만 대상으로 실행되기에, 변경하려는 데이터가 존재하지 않으면 아무런 동작을 수행하지 않는다.

5. delete

String statement = "com.example.mapper.UserMapper.deleteUser";
Integer userId = 1;
int affectedRows = sqlSession.delete(statement, userId);

기존 데이터를 삭제한다.
만약 기존 데이터에 삭제하려는 데이터가 없으면 아무런 동작을 수행하지 않는다.

 

반응형

'개발 > Spring' 카테고리의 다른 글

Spring 3장 - RESTful Service  (0) 2024.04.23
Spring 1장 - Spring Framework과 처리 프로세스  (0) 2024.04.22