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 |