> ## Content Index
> Fetch the complete content index at: https://www.popit.kr/llms.txt
> Use this file to discover other available public pages before exploring further.

# MS SQL Server에서 JPA NativeQuery 사용시 유니코드(Unicode) 문제
- URL: https://www.popit.kr/mssql-jpa-nativequery-unicode-issue/
- Published: 2017-12-12T00:14:55.000Z
- Updated: 2018-03-14T23:32:43.000Z
- Author: 유 영모
- Tags: #소프트웨어, Java, JPA, Hibernate, MSSQL

[SQL Server에서 유니코드 데이터 유형](https://technet.microsoft.com/ko-kr/library/ms191200%28v=sql.105%29.aspx?ref=popit.kr)과 JPA(구현체 Hibernate) NativeQuery를 함께 사용하는 경우 MappingException이 발생 한다. 이를 해결하는 방법을 소개 한다.

참고로 이 문제는 [JPQL(Java Persistence Query Language)](https://en.wikipedia.org/wiki/Java%5FPersistence%5FQuery%5FLanguage?ref=popit.kr)을 사용하는 경우에는 발생하지 않는다.

# 먼저 코드를 보면

MEMBER 테이블은 유니코드 데이터 유형 중 하나인 NVARCHAR인 MEMBER\_ID와 MEMBER\_NAME 컬럼을 가지고 있다.

![ERD](https://www.popit.kr/content/images/wp/2017/12/ERD-member.jpg)

```java
@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건 데이터를 가지고 있다.

![member-table-data](https://www.popit.kr/content/images/wp/2017/12/member-table-data-600x140.png)

# org.hibernate.MappingException: No Dialect mapping for JDBC type: -9

아래의 NativeQuery 테스트 코드를 실행해 보면 MappingException을 만나게 된다.  

```java
public class UnicodeTest {
    final String PERSISTENCE_UNIT_NAME = "article";
    EntityManagerFactory emf =
            Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
 
    @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());
    }
}
```

![mapping-exception](https://www.popit.kr/content/images/wp/2017/12/mapping-exception-600x61.png)

# No Dialect mapping for JDBC type: -9?

예외가 발생한 부분을 추적해 보자. 예외는 org.hibernate.dialect.TypeNames 클래스의 get()을 수행하면서 발생한 것이다.

![org.hibernate.dialect.TypeNames](https://www.popit.kr/content/images/wp/2017/12/TypeNames-class-600x231.png)

org.hibernate.dialect.TypeNames

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

![com.microsoft.sqlserver.jdbc.JDBCType](https://www.popit.kr/content/images/wp/2017/12/JDBCType-600x342.png)

com.microsoft.sqlserver.jdbc.JDBCType

# 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](https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/dialect/Dialect.html?ref=popit.kr)

RDBMS 마다 SQL을 상이하게 사용하는 부분이 있다. Hibernate는 각각의 RDBMS 마다 특수화된 Dialect를 사용하여 호환성을 확보하는 것이다.

![출처 : https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/dialect/package-summary.html](https://www.popit.kr/content/images/wp/2017/12/hibernate-Dialect-600x479.png)

출처 : https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/dialect/package-summary.html

필자의 경우 Hibernate Dialect를 org.hibernate.dialect.SQLServerDialect를 사용 하였다.  

```xhtml
<persistence-unit name="article" transaction-type="RESOURCE_LOCAL">
   //..
   <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />    
    </properties>
</persistence-unit>
```

# No Dialect mapping for JDBC type

앞서 언급한 MappingException 예외를 상기해 보면, 예외는 TypeNames에서 발생한 것이다.

**TypeNames는 Dialect의 registerColumnType을 통해 등록된다.** 

![registerColumnType](https://www.popit.kr/content/images/wp/2017/12/registerColumnType-600x232.png)

![Hibernate Dialect Class Diagram](https://www.popit.kr/content/images/wp/2017/12/dialect-class-diagram.png)

Hibernate Dialect Class Diagram

**결국 문제는 필자가 사용하는 SQLServerDialect가 JDBC Type 중 하나인 NVARCHAR를 지원하지 않아서 발생하는 것이었다.**

# 그래서 해결책은?

[StackOverFlow](https://stackoverflow.com/questions/27039300/jpa-sql-server-no-dialect-mapping-for-jdbc-type-9?ref=popit.kr)에서 답을 찾을 수 있다.

![출처 : https://stackoverflow.com/questions/27039300/jpa-sql-server-no-dialect-mapping-for-jdbc-type-9](https://www.popit.kr/content/images/wp/2017/12/stack-over-flow-600x330.png)

출처 : https://stackoverflow.com/questions/27039300/jpa-sql-server-no-dialect-mapping-for-jdbc-type-9

즉, 기존의 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](https://docs.jboss.org/hibernate/orm/3.5/api/org/hibernate/dialect/Dialect.html?ref=popit.kr)

# SQLServerUnicodeDialect

SQLServerDialect를 상속하여 SQLServerUnicodeDialect 라는 이름으로 클래스를 만들고 hibernate.dialect 에 설정해 주었다.

그리고 위의 테스트 케이스를 다시 실행해 보면 테스트를 통과 한다.  

```java
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());
    }
}

```

```xhtml
<persistence-unit name="article" transaction-type="RESOURCE_LOCAL">
   //..
   <property name="hibernate.dialect" value="ymyoo.article.sqlserver.unicode.SQLServerUnicodeDialect" />    
    </properties>
</persistence-unit>
```

# GitHub

전체 코드는 필자의 [GitHub](https://github.com/YooYoungmo/article-jpa-sqlserver-unicode?ref=popit.kr)에서 확인 가능 하다.