mysql 에서 셀렉트 리턴값은 제대로 나오고 있는데 Springboot mybatis 에서는 계속 1개밖에 안나온다..
분명 쿼리는 같고 이상없는걸 확인했는데도 이상하게 중복된 결과값이 겹친듯한 느낌으로 1개밖에 안나온다...
음...
select 결과물들은 배열로 리턴되는데 왜 mybatis 에서는 1개 밖에 리턴값이 안나오는지 잘 모르겠다. 그렇다면 코드를 한번 분석을 해보자 !
1. 왜 리턴값이 1개 밖에 안나올까?
우선 코드를 보면
<resultMap id="Follow" type="Follow">
<result property="type" column="type" />
<association property="partner" javaType="User">
<result property="user_uuid" column="partner_user_uuid" />
<result property="thumbnail" column="partner_thumbnail" />
<result property="grade" column="partner_grade" />
<result property="name" column="partner_name" />
<result property="gender" column="partner_gender" />
<result property="age" column="partner_age" />
</association>
<association property="user" javaType="User">
<result property="user_uuid" column="user_uuid" />
<result property="thumbnail" column="thumbnail" />
<result property="grade" column="grade" />
<result property="name" column="name" />
<result property="gender" column="gender" />
<result property="age" column="age" />
</association>
</resultMap>
이렇게 ResultMap 으로 빈형식으로 만들고 대입하는 구조로 진행하고 있었다.
mysql 에서의 리턴 컬럼들과 맞춰서 작업하고 있어서 중복된 부분에 대해서는 전혀 필터없이 전부 리턴값들이 잘 나올꺼라 생각했는데
그게 아니였다..
확인해보니 <association ==> 이 부분에서 뭔가 문제가 있는거 같았다.
뭔가 중복관련된 문제인듯 보여서 구글링을 시작했다..
우선 Association 에 관련해서 한번 살펴보자
association 엘리먼트는 "has-one" 타입의 관계를 다룬다.
예를들어 Blog는 하나의 Author를 가진다.
association 매핑은 다른 결과와 작동한다. 값을 가져오기 위해 대상 프로퍼티를 명시한다.
이를 보니 association 에서 "has-one" 타입의 관계를 이루고 값을 가져오기 위해서는 프로퍼티를 명시해야한다고 나와있다.
<association property="partner" column="partner_user" javaType="User"></association>
위와 같은 코드 형식으로 맞춰주어야 한다는 의미이다.
그렇다면 중복관련해서는 어떤 설정을 해주어야 할까?
![](https://t1.daumcdn.net/keditor/emoticon/friends1/large/009.gif)
2. 해결방법
우선 Mybatis에서는 이러한 부분들을 설명해주고 있다.
마이바티스는 관계를 정의하는 두 가지 방법을 제공한다.
1. 내포된(Nested) Select: 복잡한 타입을 리턴하는 다른 매핑된 SQL 구문을 실행하는 방법
2. 내포된(Nested) Results: 조인된 결과물을 반복적으로 사용하여 내포된 결과 매핑을 사용하는 방법
먼저 엘리먼트 내 프로퍼티들을 보자. 보이는 것처럼 select와 resultMap 속성만을 사용하는 간단한 결과 매핑과는 다르다.
association 엘리먼트 사용방법
속성
|
설명
|
property
|
결과 칼럼에 매핑하기 위한 필드나 프로퍼티. 자바빈 프로퍼티가 해당 이름과 일치한다면 그 프로퍼티가 사용될 것이다. 반면에 마이바티스는 해당 이름이 필드를 찾을 것이다. 점 표기를 사용하면 복잡한 프로퍼티 검색을 사용할 수 있다. 예를들어 "username"과 같이 간단하게 매핑될 수 있거나 "address.street.number"처럼 복잡하게 매핑될수도 있다.
|
javaType
|
패키지 경로를 포함한 클래스 전체명이거나 타입 별칭. 자바빈을 사용한다면 마이바티스를 타입을 찾아낼 수 있다. 반면에 HashMap으로 매핑한다면 기대하는 처리를 명확히 하기 위해 javaType을 명시해야 한다.
|
jdbcType
|
지원되는 타입 목록에서 설명하는 JDBC 타입. JDBC 타입은 insert, update 또는 delete 하는 null 입력이 가능한 칼럼에서만 필요하다. JDBC의 요구사항이지 마이바티스의 요구사항은 아니다. JDBC로 직접 코딩을 하다보면 null이 가능한 값에 이 타입을 지정할 필요가 있을 것이다.
|
typeHandler
|
이 문서 앞에서 이미 타입 핸들러에 대해 설명했다. 이 프로퍼티를 사용하면 디폴트 타입 핸들러를 오버라이드 할 수 있다. 이 값은 TypeHandler 구현체의 패키지를 포함한 전체 클래스 명이나 타입별칭이다.
|
연관(Association)을 위한 중첩된 Select
속성
|
설명
|
column
|
데이버 베이스의 칼럼명이나 별칭된 칼럼 라벨. resultSet.getString(columnName)에 전달되는 같은 문자열이다.
|
select
|
다른 매핑된 구문의 ID는 이 프로퍼티 매핑이 필요로 하는 복잡한 타입을 로드할 것이다. column 속성의 칼럼으로 부터 가져온 값은 대상 select 구문의 파라미터로 전달될 것이다.
|
fetchType
|
선택가능한 속성으로 사용가능한 값은 lazy와 eager이다. 이 속성을 사용하면 전역 설정파라미터인 lazyLoadingEnabled를 대체한다.
|
관계를 위한 내포된 결과(Nested Results)
속성
|
설명
|
resultMap
|
이 인자의 내포된 결과를 적절한 객체로 매핑할 수 있는 ResultMap의 ID이다. 다른 select 구문을 호출하기 위한 대체 방법이다. 여러개의 테이블을 조인하는 것을 하나의 ResultSet으로 매핑하도록 해준다. ResultSet은 사본을 포함할 수 있고 데이터를반복할 수도 있다. 가능하게 하기 위해서 내포된 결과를 다루도록 결과맵을 "연결"하자. 자세히 알기 위해서는 association 엘리먼트를 보라.
|
columnPrefix
|
여러개의 테이블을 조인할때 ResultSet에서 칼럼명의 중복을 피하기 위해 칼럼별칭을 사용할 수 있다. 칼럼을 외부 결과매핑에 매핑하기 위해 columnPrefix를 명시하자. 이 절의 뒤에 나오는 예제를 보자.
|
notNullColumn
|
기본적으로 자식객체는 칼럼중 적어도 하나를 null이 아닌 자식객체의 프로퍼티에 매핑할때 만들어진다. 이 속성을 사용해서 칼럼이 값을 가져야하만 하는 것을 명시해서 행위를 변경할 수 있다. 그래서 마이바티스는 이러한 칼럼이 null이 아닐때만 자식 객체를 만들것이다.
|
autoMapping
|
이 속성을 사용하면 마이바티스는 결과를 프로퍼티에 매핑할때 자동매핑을 사용할지 말지를 정한다.이 속성은 전역설정인 autoMappingBehavior를 무시하게 한다. 외부 결과매핑에는 영향을 주지 않는다. 그래서 select나 resultMap속성을 함께 사용하는 것은 의미가 없다. 디폴트 값은 unset이다.
|
id 엘리먼트는 내포된 결과 매칭에서 매우 중요한 역할을 담당한다.
결과 중 유일한 것을 찾아내기 위한 한개 이상의 프로퍼티를 명시해야만 한다.
가능하면 결과 중 유일한 것을 찾아낼 수 있는 프로퍼티들을 선택하라. 기본키가 가장 좋은 선택이 될 수 있다.
어쨌든 재사용할 필요가 있거나 한개의 resultMap에 결과 매핑을 함께 위치시키고자 한다면 association 결과 매핑을 내포시킬수 있다.
![](https://t1.daumcdn.net/keditor/emoticon/friends1/large/008.gif)
그래서 ResultMap 에 대한 중복문제는 association 이라는 구문에서 뭔가 작업을 해주어야 하는구나 라는것을 직감하게 되었고
다른 구조로 작업을 하였다.
<resultMap id="Follow" type="Follow">
<result property="type" column="type" />
<association property="user" column="user_uuid" javaType="User" select="findUser" />
<association property="partner" column="partner_user_uuid" javaType="User" select="findUser" />
</resultMap>
이렇게 바꾸었더니.... hasmap 관련 중복 키 문제를 해결하게 됐다. !!!!!!!
![](https://t1.daumcdn.net/keditor/emoticon/friends1/large/007.gif)
그럼 다른 유용한 기능들은 무엇이 있는지 알아보도록 하자
별책부록
1. Dynamic SQL (동적 SQL)
- 조건에 따라 쿼리를 동적으로 만들 수 있어.
- <if>, <choose>, <when>, <otherwise>, <where>, <set> 등을 사용해서 쿼리 조건을 동적으로 바꿀 수 있어.
예시:
<select id="selectUser" resultType="User">
SELECT * FROM users
<where>
<if test="name != null">AND name = #{name}</if>
<if test="age != null">AND age = #{age}</if>
</where>
</select>
2. SQL Fragment (SQL 조각)
- 공통으로 사용되는 SQL 부분을 재사용할 때 유용.
- <sql> 태그로 공통 SQL을 정의하고 <include> 태그로 포함할 수 있어.
예시:
<sql id="userColumns">
id, name, age
</sql>
<select id="selectUser" resultType="User">
SELECT <include refid="userColumns"/> FROM users WHERE id = #{id}
</select>
3. ResultType vs ResultMap
- ResultType: 간단한 매핑, 결과를 직접 자바 객체로 반환.
- ResultMap: 복잡한 매핑, 컬럼과 객체의 프로퍼티가 다를 때 사용.
예시:
<select id="selectUser" resultMap="userResultMap">
SELECT id, user_name, user_age FROM users
</select>
<resultMap id="userResultMap" type="User">
<result property="name" column="user_name"/>
<result property="age" column="user_age"/>
</resultMap>
4. Association / Collection (연관 객체, 컬렉션)
- 연관된 객체나 컬렉션을 매핑할 때 사용.
- association은 1:1 관계, collection은 1:N 관계에 사용.
예시:
<resultMap id="orderResultMap" type="Order">
<id property="id" column="order_id"/>
<result property="orderDate" column="order_date"/>
<association property="user" column="user_id" javaType="User" select="selectUser"/>
<collection property="items" ofType="Item" select="selectItemsForOrder"/>
</resultMap>
5. Lazy Loading
- 기본적으로 Lazy Loading은 객체를 실제로 사용할 때 DB에서 값을 가져오도록 설정할 수 있어.
- lazy 속성으로 설정.
예시:
<resultMap id="userResultMap" type="User" lazy="true">
<result property="name" column="user_name"/>
<result property="address" column="user_address"/>
</resultMap>
6. Cache (캐시)
- MyBatis는 1차 캐시와 2차 캐시를 제공해.
- 1차 캐시는 세션 범위, 2차 캐시는 여러 세션에서 공유 가능.
예시:
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<cache/>
7. TypeHandler
- 자바 타입과 DB 타입 간의 변환을 커스터마이즈할 수 있어.
- DB에서 읽을 때나 자바 객체로 매핑할 때 사용.
예시:
@MappedTypes(MyEnum.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyEnumTypeHandler extends BaseTypeHandler<MyEnum> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, MyEnum parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter.name());
}
@Override
public MyEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
return MyEnum.valueOf(rs.getString(columnName));
}
}
8. Transaction Management (트랜잭션 관리)
- MyBatis는 트랜잭션을 명시적으로 관리할 수 있어. SqlSession의 commit()과 rollback()으로 트랜잭션을 처리해.
예시:
SqlSession session = sqlSessionFactory.openSession();
try {
// DB 작업
session.commit(); // 커밋
} catch (Exception e) {
session.rollback(); // 롤백
} finally {
session.close();
}
9. Batch Update (배치 업데이트)
- 여러 개의 쿼리를 한 번에 실행할 수 있어.
- 대량 데이터 처리 시 유용.
예시:
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
for (User user : userList) {
session.update("updateUser", user);
}
session.commit();
} finally {
session.close();
}
10. Multiple Mappers
- 여러 개의 Mapper를 한 번에 사용할 수 있어. SqlSession에서 한 번에 여러 Mapper를 호출할 수 있어.
예시:
<mapper namespace="com.example.UserMapper">
<select id="selectUser" resultType="User">
SELECT * FROM users WHERE id = #{id}
</select>
</mapper>
<mapper namespace="com.example.OrderMapper">
<select id="selectOrder" resultType="Order">
SELECT * FROM orders WHERE user_id = #{userId}
</select>
</mapper>
요약
- Dynamic SQL로 동적으로 쿼리 작성.
- SQL Fragment로 공통 SQL 재사용.
- Association/Collection으로 연관된 객체 처리.
- Lazy Loading으로 지연 로딩.
- Cache로 성능 향상.
- TypeHandler로 타입 변환 커스터마이징.
- Transaction Management로 트랜잭션 제어.
- Batch Update로 배치 처리.
- 여러 개의 Mapper를 한 번에 사용할 수 있어.
이렇게 MyBatis에서 자주 쓰이는 유용한 기능들을 알아봤어!
'Mysql' 카테고리의 다른 글
Centos 7. 에서 Mysql data import 방법 (2) | 2024.10.14 |
---|