From 10a35efe12d415ea429ce65b353d7267920be444 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Fri, 23 Mar 2018 00:15:32 +0900 Subject: [PATCH 1/3] A new type handler for SQLXML data type. --- .../apache/ibatis/type/SqlxmlTypeHandler.java | 57 ++++++++ .../ibatis/type/TypeHandlerRegistry.java | 2 + .../ibatis/type/SqlxmlTypeHandlerTest.java | 131 ++++++++++++++++++ .../ibatis/type/SqlxmlTypeHandlerTest.sql | 25 ++++ 4 files changed, 215 insertions(+) create mode 100644 src/main/java/org/apache/ibatis/type/SqlxmlTypeHandler.java create mode 100644 src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.java create mode 100644 src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.sql 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..7214f26c474 --- /dev/null +++ b/src/main/java/org/apache/ibatis/type/SqlxmlTypeHandler.java @@ -0,0 +1,57 @@ +/** + * 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; + +public class SqlxmlTypeHandler extends BaseTypeHandler { + + @Override + public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) + throws SQLException { + SQLXML sqlxml = ps.getConnection().createSQLXML(); + sqlxml.setString(parameter); + ps.setSQLXML(i, sqlxml); + 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 { + String result = sqlxml.getString(); + sqlxml.free(); + return result; + } + +} 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..4d1d21e7cf5 --- /dev/null +++ b/src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.java @@ -0,0 +1,131 @@ +/** + * 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.Param; +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 shouldRetrieveXml() throws Exception { + SqlSession session = sqlSessionFactory.openSession(); + try { + Mapper mapper = session.getMapper(Mapper.class); + String content = mapper.select(1); + assertEquals("XML data", + content); + } finally { + session.close(); + } + } + + @Test + public void shouldInsertXml() throws Exception { + final Integer id = 100; + final String content = "Save XMLGet XML"; + // Insert + { + SqlSession session = sqlSessionFactory.openSession(); + try { + Mapper mapper = session.getMapper(Mapper.class); + mapper.insert(id, content); + session.commit(); + } finally { + session.close(); + } + } + // Select to verify + { + SqlSession session = sqlSessionFactory.openSession(); + try { + Mapper mapper = session.getMapper(Mapper.class); + String result = mapper.select(id); + assertEquals(content, result); + } finally { + session.close(); + } + } + } + + interface Mapper { + @Select("select content from mbtest.test_sqlxml where id = #{id}") + String select(Integer id); + + @Insert("insert into mbtest.test_sqlxml (id, content) values (#{id}, #{content,jdbcType=SQLXML})") + void insert(@Param("id") Integer id, @Param("content") String 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..f0479620e74 --- /dev/null +++ b/src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.sql @@ -0,0 +1,25 @@ +-- +-- 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'); From f7b065e3186bd969cebe336cc1364530e587e2d6 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Fri, 23 Mar 2018 02:15:34 +0900 Subject: [PATCH 2/3] Requested changes. --- .../apache/ibatis/type/SqlxmlTypeHandler.java | 27 +++++++-- .../ibatis/type/SqlxmlTypeHandlerTest.java | 58 +++++++++++++++---- .../ibatis/type/SqlxmlTypeHandlerTest.sql | 3 + 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/apache/ibatis/type/SqlxmlTypeHandler.java b/src/main/java/org/apache/ibatis/type/SqlxmlTypeHandler.java index 7214f26c474..84aad15fe0f 100644 --- a/src/main/java/org/apache/ibatis/type/SqlxmlTypeHandler.java +++ b/src/main/java/org/apache/ibatis/type/SqlxmlTypeHandler.java @@ -22,15 +22,24 @@ import java.sql.SQLException; import java.sql.SQLXML; +/** + * Convert String to/from SQLXML. + * + * @since 3.4.5 + * @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(); - sqlxml.setString(parameter); - ps.setSQLXML(i, sqlxml); - sqlxml.free(); + try { + sqlxml.setString(parameter); + ps.setSQLXML(i, sqlxml); + } finally { + sqlxml.free(); + } } @Override @@ -49,9 +58,15 @@ public String getNullableResult(CallableStatement cs, int columnIndex) throws SQ } protected String sqlxmlToString(SQLXML sqlxml) throws SQLException { - String result = sqlxml.getString(); - sqlxml.free(); - return result; + if (sqlxml == null) { + return null; + } + try { + String result = sqlxml.getString(); + return result; + } finally { + sqlxml.free(); + } } } diff --git a/src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.java b/src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.java index 4d1d21e7cf5..4219e74bbda 100644 --- a/src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.java +++ b/src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.java @@ -24,7 +24,6 @@ import java.util.Collections; import org.apache.ibatis.annotations.Insert; -import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.datasource.unpooled.UnpooledDataSource; import org.apache.ibatis.io.Resources; @@ -81,20 +80,32 @@ public static void tearDown() { } @Test - public void shouldRetrieveXml() throws Exception { + public void shouldReturnXmlAsString() throws Exception { SqlSession session = sqlSessionFactory.openSession(); try { Mapper mapper = session.getMapper(Mapper.class); - String content = mapper.select(1); + XmlBean bean = mapper.select(1); assertEquals("XML data", - content); + bean.getContent()); } finally { session.close(); } } @Test - public void shouldInsertXml() throws Exception { + 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 @@ -102,7 +113,10 @@ public void shouldInsertXml() throws Exception { SqlSession session = sqlSessionFactory.openSession(); try { Mapper mapper = session.getMapper(Mapper.class); - mapper.insert(id, content); + XmlBean bean = new XmlBean(); + bean.setId(id); + bean.setContent(content); + mapper.insert(bean); session.commit(); } finally { session.close(); @@ -113,8 +127,8 @@ public void shouldInsertXml() throws Exception { SqlSession session = sqlSessionFactory.openSession(); try { Mapper mapper = session.getMapper(Mapper.class); - String result = mapper.select(id); - assertEquals(content, result); + XmlBean bean = mapper.select(id); + assertEquals(content, bean.getContent()); } finally { session.close(); } @@ -122,10 +136,32 @@ public void shouldInsertXml() throws Exception { } interface Mapper { - @Select("select content from mbtest.test_sqlxml where id = #{id}") - String select(Integer id); + @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(@Param("id") Integer id, @Param("content") String content); + 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 index f0479620e74..aae14168197 100644 --- a/src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.sql +++ b/src/test/java/org/apache/ibatis/type/SqlxmlTypeHandlerTest.sql @@ -23,3 +23,6 @@ CREATE TABLE mbtest.test_sqlxml ( INSERT INTO mbtest.test_sqlxml (id, content) VALUES (1, 'XML data'); + +INSERT INTO mbtest.test_sqlxml (id, content) +VALUES (2, NULL); From 54d4e83d4ad91c83b6bcc0291f0bb50a63b55404 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 24 Mar 2018 10:18:09 +0900 Subject: [PATCH 3/3] Wrong version. --- src/main/java/org/apache/ibatis/type/SqlxmlTypeHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/ibatis/type/SqlxmlTypeHandler.java b/src/main/java/org/apache/ibatis/type/SqlxmlTypeHandler.java index 84aad15fe0f..0be63cdf572 100644 --- a/src/main/java/org/apache/ibatis/type/SqlxmlTypeHandler.java +++ b/src/main/java/org/apache/ibatis/type/SqlxmlTypeHandler.java @@ -25,7 +25,7 @@ /** * Convert String to/from SQLXML. * - * @since 3.4.5 + * @since 3.5.0 * @author Iwao AVE! */ public class SqlxmlTypeHandler extends BaseTypeHandler {