diff --git a/src/main/java/org/apache/ibatis/type/SqlxmlTypeHandler.java b/src/main/java/org/apache/ibatis/type/SqlxmlTypeHandler.java
new file mode 100644
index 00000000000..0be63cdf572
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/type/SqlxmlTypeHandler.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright 2009-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ibatis.type;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+
+/**
+ * Convert String
to/from SQLXML
.
+ *
+ * @since 3.5.0
+ * @author Iwao AVE!
+ */
+public class SqlxmlTypeHandler extends BaseTypeHandler {
+
+ @Override
+ public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
+ throws SQLException {
+ SQLXML sqlxml = ps.getConnection().createSQLXML();
+ try {
+ sqlxml.setString(parameter);
+ ps.setSQLXML(i, sqlxml);
+ } finally {
+ sqlxml.free();
+ }
+ }
+
+ @Override
+ public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
+ return sqlxmlToString(rs.getSQLXML(columnName));
+ }
+
+ @Override
+ public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
+ return sqlxmlToString(rs.getSQLXML(columnIndex));
+ }
+
+ @Override
+ public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
+ return sqlxmlToString(cs.getSQLXML(columnIndex));
+ }
+
+ protected String sqlxmlToString(SQLXML sqlxml) throws SQLException {
+ if (sqlxml == null) {
+ return null;
+ }
+ try {
+ String result = sqlxml.getString();
+ return result;
+ } finally {
+ sqlxml.free();
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java b/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java
index 3d3385aa222..a6f754ac7c7 100644
--- a/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java
+++ b/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java
@@ -145,6 +145,8 @@ public TypeHandlerRegistry() {
register(java.sql.Time.class, new SqlTimeTypeHandler());
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
+ register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());
+
// mybatis-typehandlers-jsr310
if (Jdk.dateAndTimeApiExists) {
this.register(Instant.class, InstantTypeHandler.class);
diff --git a/src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.java b/src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.java
new file mode 100644
index 00000000000..4219e74bbda
--- /dev/null
+++ b/src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.java
@@ -0,0 +1,167 @@
+/**
+ * Copyright 2009-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ibatis.type;
+
+import static org.junit.Assert.*;
+
+import java.io.Reader;
+import java.nio.file.Paths;
+import java.sql.Connection;
+import java.util.Collections;
+
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
+import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.jdbc.ScriptRunner;
+import org.apache.ibatis.mapping.Environment;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.apache.ibatis.session.SqlSessionFactoryBuilder;
+import org.apache.ibatis.test.EmbeddedPostgresqlTests;
+import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import ru.yandex.qatools.embed.postgresql.EmbeddedPostgres;
+import ru.yandex.qatools.embed.postgresql.util.SocketUtil;
+
+@Category(EmbeddedPostgresqlTests.class)
+public class SqlxmlTypeHandlerTest {
+ private static final EmbeddedPostgres postgres = new EmbeddedPostgres();
+
+ private static SqlSessionFactory sqlSessionFactory;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ // Launch PostgreSQL server. Download / unarchive if necessary.
+ String url = postgres.start(
+ EmbeddedPostgres.cachedRuntimeConfig(Paths.get(System.getProperty("java.io.tmpdir"), "pgembed")), "localhost",
+ SocketUtil.findFreePort(), "postgres_sqlxml", "postgres", "root", Collections.emptyList());
+
+ Configuration configuration = new Configuration();
+ Environment environment = new Environment("development", new JdbcTransactionFactory(), new UnpooledDataSource(
+ "org.postgresql.Driver", url, null));
+ configuration.setEnvironment(environment);
+ configuration.setUseGeneratedKeys(true);
+ configuration.addMapper(Mapper.class);
+ sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
+
+ try (SqlSession session = sqlSessionFactory.openSession();
+ Connection conn = session.getConnection();
+ Reader reader = Resources
+ .getResourceAsReader("org/apache/ibatis/type/SqlxmlTypeHandlerTest.sql")) {
+ ScriptRunner runner = new ScriptRunner(conn);
+ runner.setLogWriter(null);
+ runner.runScript(reader);
+ }
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ postgres.stop();
+ }
+
+ @Test
+ public void shouldReturnXmlAsString() throws Exception {
+ SqlSession session = sqlSessionFactory.openSession();
+ try {
+ Mapper mapper = session.getMapper(Mapper.class);
+ XmlBean bean = mapper.select(1);
+ assertEquals("XML data",
+ bean.getContent());
+ } finally {
+ session.close();
+ }
+ }
+
+ @Test
+ public void shouldReturnNull() throws Exception {
+ SqlSession session = sqlSessionFactory.openSession();
+ try {
+ Mapper mapper = session.getMapper(Mapper.class);
+ XmlBean bean = mapper.select(2);
+ assertNull(bean.getContent());
+ } finally {
+ session.close();
+ }
+ }
+
+ @Test
+ public void shouldInsertXmlString() throws Exception {
+ final Integer id = 100;
+ final String content = "Save XMLGet XML";
+ // Insert
+ {
+ SqlSession session = sqlSessionFactory.openSession();
+ try {
+ Mapper mapper = session.getMapper(Mapper.class);
+ XmlBean bean = new XmlBean();
+ bean.setId(id);
+ bean.setContent(content);
+ mapper.insert(bean);
+ session.commit();
+ } finally {
+ session.close();
+ }
+ }
+ // Select to verify
+ {
+ SqlSession session = sqlSessionFactory.openSession();
+ try {
+ Mapper mapper = session.getMapper(Mapper.class);
+ XmlBean bean = mapper.select(id);
+ assertEquals(content, bean.getContent());
+ } finally {
+ session.close();
+ }
+ }
+ }
+
+ interface Mapper {
+ @Select("select id, content from mbtest.test_sqlxml where id = #{id}")
+ XmlBean select(Integer id);
+
+ @Insert("insert into mbtest.test_sqlxml (id, content) values (#{id}, #{content,jdbcType=SQLXML})")
+ void insert(XmlBean bean);
+ }
+
+ public static class XmlBean {
+ private Integer id;
+
+ private String content;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+ }
+}
diff --git a/src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.sql b/src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.sql
new file mode 100644
index 00000000000..aae14168197
--- /dev/null
+++ b/src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.sql
@@ -0,0 +1,28 @@
+--
+-- Copyright 2009-2018 the original author or authors.
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+CREATE SCHEMA mbtest;
+
+CREATE TABLE mbtest.test_sqlxml (
+ id serial PRIMARY KEY,
+ content XML
+);
+
+INSERT INTO mbtest.test_sqlxml (id, content)
+VALUES (1, 'XML data');
+
+INSERT INTO mbtest.test_sqlxml (id, content)
+VALUES (2, NULL);