Skip to content

Commit f7ce783

Browse files
authored
Merge pull request mybatis#1548 from dirk-olmes/ArrayTypeHandlerImprovements
Improve ArrayTypeHandler
2 parents 002a525 + 9e85477 commit f7ce783

File tree

8 files changed

+363
-2
lines changed

8 files changed

+363
-2
lines changed

src/main/java/org/apache/ibatis/type/ArrayTypeHandler.java

+66-2
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,88 @@
1515
*/
1616
package org.apache.ibatis.type;
1717

18+
import java.math.BigDecimal;
19+
import java.math.BigInteger;
20+
import java.net.URL;
1821
import java.sql.Array;
1922
import java.sql.CallableStatement;
2023
import java.sql.PreparedStatement;
2124
import java.sql.ResultSet;
2225
import java.sql.SQLException;
26+
import java.sql.Time;
27+
import java.sql.Timestamp;
28+
import java.time.LocalDate;
29+
import java.time.LocalDateTime;
30+
import java.time.LocalTime;
31+
import java.time.OffsetDateTime;
32+
import java.time.OffsetTime;
33+
import java.util.Calendar;
34+
import java.util.concurrent.ConcurrentHashMap;
2335

2436
/**
2537
* @author Clinton Begin
2638
*/
2739
public class ArrayTypeHandler extends BaseTypeHandler<Object> {
2840

41+
private static final ConcurrentHashMap<Class<?>, String> STANDARD_MAPPING;
42+
static {
43+
STANDARD_MAPPING = new ConcurrentHashMap<>();
44+
STANDARD_MAPPING.put(BigDecimal.class, JdbcType.NUMERIC.name());
45+
STANDARD_MAPPING.put(BigInteger.class, JdbcType.BIGINT.name());
46+
STANDARD_MAPPING.put(boolean.class, JdbcType.BOOLEAN.name());
47+
STANDARD_MAPPING.put(Boolean.class, JdbcType.BOOLEAN.name());
48+
STANDARD_MAPPING.put(byte[].class, JdbcType.VARBINARY.name());
49+
STANDARD_MAPPING.put(byte.class, JdbcType.TINYINT.name());
50+
STANDARD_MAPPING.put(Byte.class, JdbcType.TINYINT.name());
51+
STANDARD_MAPPING.put(Calendar.class, JdbcType.TIMESTAMP.name());
52+
STANDARD_MAPPING.put(java.sql.Date.class, JdbcType.DATE.name());
53+
STANDARD_MAPPING.put(java.util.Date.class, JdbcType.TIMESTAMP.name());
54+
STANDARD_MAPPING.put(double.class, JdbcType.DOUBLE.name());
55+
STANDARD_MAPPING.put(Double.class, JdbcType.DOUBLE.name());
56+
STANDARD_MAPPING.put(float.class, JdbcType.REAL.name());
57+
STANDARD_MAPPING.put(Float.class, JdbcType.REAL.name());
58+
STANDARD_MAPPING.put(int.class, JdbcType.INTEGER.name());
59+
STANDARD_MAPPING.put(Integer.class, JdbcType.INTEGER.name());
60+
STANDARD_MAPPING.put(LocalDate.class, JdbcType.DATE.name());
61+
STANDARD_MAPPING.put(LocalDateTime.class, JdbcType.TIMESTAMP.name());
62+
STANDARD_MAPPING.put(LocalTime.class, JdbcType.TIME.name());
63+
STANDARD_MAPPING.put(long.class, JdbcType.BIGINT.name());
64+
STANDARD_MAPPING.put(Long.class, JdbcType.BIGINT.name());
65+
STANDARD_MAPPING.put(OffsetDateTime.class, JdbcType.TIMESTAMP_WITH_TIMEZONE.name());
66+
STANDARD_MAPPING.put(OffsetTime.class, JdbcType.TIME_WITH_TIMEZONE.name());
67+
STANDARD_MAPPING.put(Short.class, JdbcType.SMALLINT.name());
68+
STANDARD_MAPPING.put(String.class, JdbcType.VARCHAR.name());
69+
STANDARD_MAPPING.put(Time.class, JdbcType.TIME.name());
70+
STANDARD_MAPPING.put(Timestamp.class, JdbcType.TIMESTAMP.name());
71+
STANDARD_MAPPING.put(URL.class, JdbcType.DATALINK.name());
72+
}
73+
2974
public ArrayTypeHandler() {
3075
super();
3176
}
3277

3378
@Override
34-
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
35-
ps.setArray(i, (Array) parameter);
79+
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
80+
throws SQLException {
81+
if (parameter instanceof Array) {
82+
// it's the user's responsibility to properly free() the Array instance
83+
ps.setArray(i, (Array) parameter);
84+
} else {
85+
if (!parameter.getClass().isArray()) {
86+
throw new TypeException(
87+
"ArrayType Handler requires SQL array or java array parameter and does not support type "
88+
+ parameter.getClass());
89+
}
90+
Class<?> componentType = parameter.getClass().getComponentType();
91+
String arrayTypeName = resolveTypeName(componentType);
92+
Array array = ps.getConnection().createArrayOf(arrayTypeName, (Object[]) parameter);
93+
ps.setArray(i, array);
94+
array.free();
95+
}
96+
}
97+
98+
protected String resolveTypeName(Class<?> type) {
99+
return STANDARD_MAPPING.getOrDefault(type, JdbcType.JAVA_OBJECT.name());
36100
}
37101

38102
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* Copyright 2009-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.submitted.array_type_handler;
17+
18+
import static org.junit.jupiter.api.Assertions.assertEquals;
19+
import static org.junit.jupiter.api.Assertions.assertNull;
20+
21+
import java.io.Reader;
22+
23+
import org.apache.ibatis.BaseDataTest;
24+
import org.apache.ibatis.io.Resources;
25+
import org.apache.ibatis.session.SqlSession;
26+
import org.apache.ibatis.session.SqlSessionFactory;
27+
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
28+
import org.junit.jupiter.api.BeforeEach;
29+
import org.junit.jupiter.api.Test;
30+
31+
public class ArrayTypeHandlerTest {
32+
33+
private SqlSessionFactory sqlSessionFactory;
34+
35+
@BeforeEach
36+
public void setUp() throws Exception {
37+
try (Reader reader = Resources
38+
.getResourceAsReader("org/apache/ibatis/submitted/array_type_handler/mybatis-config.xml")) {
39+
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
40+
}
41+
42+
BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
43+
"org/apache/ibatis/submitted/array_type_handler/CreateDB.sql");
44+
}
45+
46+
@Test
47+
public void shouldInsertArrayValue() throws Exception {
48+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
49+
User user = new User();
50+
user.setId(1);
51+
user.setName("User 1");
52+
user.setNicknames(new String[] { "User", "one" });
53+
54+
Mapper mapper = sqlSession.getMapper(Mapper.class);
55+
mapper.insert(user);
56+
sqlSession.commit();
57+
58+
int usersInDatabase = mapper.getUserCount();
59+
assertEquals(1, usersInDatabase);
60+
61+
Integer nicknameCount = mapper.getNicknameCount();
62+
assertEquals(2, nicknameCount);
63+
}
64+
}
65+
66+
@Test
67+
public void shouldInsertNullValue() throws Exception {
68+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
69+
User user = new User();
70+
user.setId(1);
71+
user.setName("User 1");
72+
// note how the user does not have nicknames
73+
74+
Mapper mapper = sqlSession.getMapper(Mapper.class);
75+
mapper.insert(user);
76+
sqlSession.commit();
77+
78+
int usersInDatabase = mapper.getUserCount();
79+
assertEquals(1, usersInDatabase);
80+
81+
Integer nicknameCount = mapper.getNicknameCount();
82+
assertNull(nicknameCount);
83+
}
84+
}
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--
2+
-- Copyright 2009-2019 the original author or authors.
3+
--
4+
-- Licensed under the Apache License, Version 2.0 (the "License");
5+
-- you may not use this file except in compliance with the License.
6+
-- You may obtain a copy of the License at
7+
--
8+
-- http://www.apache.org/licenses/LICENSE-2.0
9+
--
10+
-- Unless required by applicable law or agreed to in writing, software
11+
-- distributed under the License is distributed on an "AS IS" BASIS,
12+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
-- See the License for the specific language governing permissions and
14+
-- limitations under the License.
15+
--
16+
17+
drop table users if exists;
18+
19+
create table users (
20+
id int,
21+
name varchar(20),
22+
nicknames varchar(20) array
23+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Copyright 2009-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.submitted.array_type_handler;
17+
18+
public interface Mapper {
19+
20+
void insert(User user);
21+
22+
int getUserCount();
23+
24+
/**
25+
* HSQL returns NULL when asked for the cardinality of an array column with NULL value :-(
26+
*/
27+
Integer getNicknameCount();
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Copyright 2009-2019 the original author or authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
-->
19+
<!DOCTYPE mapper
20+
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
21+
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
22+
23+
<mapper namespace="org.apache.ibatis.submitted.array_type_handler.Mapper">
24+
25+
<insert id="insert"
26+
parameterType="org.apache.ibatis.submitted.array_type_handler.User">
27+
insert into users
28+
(id, name, nicknames)
29+
values
30+
(#{id}, #{name}, #{nicknames,typeHandler=org.apache.ibatis.type.ArrayTypeHandler})
31+
</insert>
32+
33+
<select id="getUserCount" resultType="int">
34+
select count(*) from users
35+
</select>
36+
37+
<select id="getNicknameCount" resultType="int">
38+
select cardinality(nicknames) from users where id = 1
39+
</select>
40+
41+
</mapper>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* Copyright 2009-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.submitted.array_type_handler;
17+
18+
public class User {
19+
20+
private Integer id;
21+
private String name;
22+
private String[] nicknames;
23+
24+
public Integer getId() {
25+
return id;
26+
}
27+
28+
public void setId(Integer id) {
29+
this.id = id;
30+
}
31+
32+
public String getName() {
33+
return name;
34+
}
35+
36+
public void setName(String name) {
37+
this.name = name;
38+
}
39+
40+
public String[] getNicknames() {
41+
return nicknames;
42+
}
43+
44+
public void setNicknames(String[] nicknames) {
45+
this.nicknames = nicknames;
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<!--
3+
4+
Copyright 2009-2019 the original author or authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
-->
19+
<!DOCTYPE configuration
20+
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
21+
"http://mybatis.org/dtd/mybatis-3-config.dtd">
22+
23+
<configuration>
24+
25+
<environments default="development">
26+
<environment id="development">
27+
<transactionManager type="JDBC"></transactionManager>
28+
<dataSource type="UNPOOLED">
29+
<property name="driver" value="org.hsqldb.jdbcDriver" />
30+
<property name="url" value="jdbc:hsqldb:mem:arrayrtypehandler" />
31+
<property name="username" value="sa" />
32+
</dataSource>
33+
</environment>
34+
</environments>
35+
36+
<mappers>
37+
<mapper class="org.apache.ibatis.submitted.array_type_handler.Mapper" />
38+
</mappers>
39+
40+
</configuration>

0 commit comments

Comments
 (0)