MS SQL Server에서 JPA NativeQuery 사용시 유니코드(Unicode) 문제
2020.01.24 18:47

MS SQL Server에서 JPA NativeQuery 사용시 유니코드(Unicode) 문제
article-jpa-sqlserver-unicode-master.zip
참고로 이 문제는 JPQL(Java Persistence Query Language)을 사용하는 경우에는 발생하지 않는다.
먼저 코드를 보면
MEMBER 테이블은 유니코드 데이터 유형 중 하나인 NVARCHAR인 MEMBER_ID와 MEMBER_NAME 컬럼을 가지고 있다.

@Entity
@Table(name = "MEMBER")
public class Member {
@Id
@Column(name="MEMBER_ID", columnDefinition = "NVARCHAR(20)")
private String memberId;
@Column(name="MEMBER_NAME", columnDefinition = "NVARCHAR(20)")
private String memberName;
@Column(name="AGE")
private Integer age;
// ..
}
그리고 MEMBER 테이블에는 3건 데이터가 있다.

org.hibernate.MappingException: No Dialect mapping for JDBC type: -9
아래의 NativeQuery 테스트 코드를 실행해 보면 MappingException을 만나게 된다.
@Test
public void test_native_sql() {
// given
final int age = 30;
// when
EntityManager em = this.emf.createEntityManager();
Query nativeQuery =
em.createNativeQuery("select member_id, member_name, age " +
"from member " +
"where age=:age")
.setParameter("age", age);
List<Member> members = nativeQuery.getResultList();
// then
Assert.assertEquals(2, members.size());
}

No Dialect mapping for JDBC type: -9?
예외가 발생한 부분을 추적해 보자. 예외는 org.hibernate.dialect.TypeNames 클래스의 get()을 수행하면서 발생한 것이다.

그리고 여기서 JDBC type -9 가 의미하는 것은 바로 com.microsoft.sqlserver.jdbc.JDBCType의 NVARCHAR 이다.

Hibernate Dialect
No Dialect mapping for JDBC type 을 알아보기 전에 Hibernate Dialect(방언)에 대해 알아보자.
Hibernate 문서는 Dialect를 아래와 같이 정의 하고 있다.
Represents a dialect of SQL implemented by a particular RDBMS. Subclasses implement Hibernate compatibility with different systems. — https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/dialect/Dialect.html
RDBMS 마다 SQL을 상이하게 사용하는 부분이 있다. Hibernate는 각각의 RDBMS 마다 특수화된 Dialect를 사용하여 호환성을 확보하는 것이다.

필자의 경우 Hibernate Dialect를 org.hibernate.dialect.SQLServerDialect를 사용 하였다.
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />
No Dialect mapping for JDBC type
앞서 언급한 MappingException 예외를 상기해 보면, 예외는 TypeNames에서 발생한 것이다.
TypeNames는 Dialect의 registerColumnType을 통해 등록된다.


결국 문제는 필자가 사용하는 SQLServerDialect가 JDBC Type 중 하나인 NVARCHAR를 지원하지 않아서 발생하는 것이었다.
그래서 해결책은?
StackOverFlow에서 답을 찾을 수 있다.

즉, 기존의 Dialect(방언) 상속하여 유니코드 관련 데이터 타입을 추가로 등록해 주라는 것이다.
이와 관련되서 Hibernate Dialect 문서에 언급이 있다.
Subclasses should provide a public default constructor that register() a set of type mappings and default Hibernate properties. — https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/dialect/Dialect.html
SQLServerUnicodeDialect
SQLServerDialect를 상속하여 SQLServerUnicodeDialect 라는 이름으로 클래스를 만들고 hibernate.dialect 에 설정해 주었다.
그리고 위의 테스트 케이스를 다시 실행해 보면 테스트를 통과 한다.
public class SQLServerUnicodeDialect extends SQLServerDialect { public SQLServerUnicodeDialect() { registerHibernateType(Types.NCHAR, StandardBasicTypes.CHARACTER.getName()); registerHibernateType(Types.NCHAR, 1, StandardBasicTypes.CHARACTER.getName()); registerHibernateType(Types.NCHAR, 255, StandardBasicTypes.STRING.getName()); registerHibernateType(Types.NVARCHAR, StandardBasicTypes.STRING.getName()); registerHibernateType(Types.LONGNVARCHAR, StandardBasicTypes.TEXT.getName()); registerHibernateType(Types.NCLOB, StandardBasicTypes.CLOB.getName()); } }// persistence.xml <property name="hibernate.dialect" value="ymyoo.article.sqlserver.unicode.SQLServerUnicodeDialect" />
GitHub
전체 코드는 필자의 GitHub에서 확인 가능 하다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.










