Skip to content

Commit 123c42e

Browse files
authored
Merge pull request #3388 from harawata/gh/566-oracle-nested-cursor
Support nested cursor
2 parents 7d38134 + 03dc3e4 commit 123c42e

File tree

19 files changed

+973
-82
lines changed

19 files changed

+973
-82
lines changed

src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java

+44-3
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap,
532532
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
533533
for (ResultMapping propertyMapping : propertyMappings) {
534534
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
535-
if (propertyMapping.getNestedResultMapId() != null) {
535+
if (propertyMapping.getNestedResultMapId() != null && !JdbcType.CURSOR.equals(propertyMapping.getJdbcType())) {
536536
// the user added a column attribute to a nested result map, ignore it
537537
column = null;
538538
}
@@ -568,6 +568,11 @@ private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject
568568
if (propertyMapping.getNestedQueryId() != null) {
569569
return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
570570
}
571+
if (JdbcType.CURSOR.equals(propertyMapping.getJdbcType())) {
572+
List<Object> results = getNestedCursorValue(rs, propertyMapping, columnPrefix);
573+
linkObjects(metaResultObject, propertyMapping, results.get(0), true);
574+
return metaResultObject.getValue(propertyMapping.getProperty());
575+
}
571576
if (propertyMapping.getResultSet() != null) {
572577
addPendingChildRelation(rs, metaResultObject, propertyMapping); // TODO is that OK?
573578
return DEFERRED;
@@ -578,6 +583,18 @@ private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject
578583
}
579584
}
580585

586+
private List<Object> getNestedCursorValue(ResultSet rs, ResultMapping propertyMapping, String parentColumnPrefix)
587+
throws SQLException {
588+
final String column = prependPrefix(propertyMapping.getColumn(), parentColumnPrefix);
589+
ResultMap nestedResultMap = resolveDiscriminatedResultMap(rs,
590+
configuration.getResultMap(propertyMapping.getNestedResultMapId()),
591+
getColumnPrefix(parentColumnPrefix, propertyMapping));
592+
ResultSetWrapper rsw = new ResultSetWrapper(rs.getObject(column, ResultSet.class), configuration);
593+
List<Object> results = new ArrayList<>();
594+
handleResultSet(rsw, nestedResultMap, results, null);
595+
return results;
596+
}
597+
581598
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap,
582599
MetaObject metaObject, String columnPrefix) throws SQLException {
583600
final String mapKey = resultMap.getId() + ":" + columnPrefix;
@@ -761,6 +778,15 @@ Object createParameterizedResultObject(ResultSetWrapper rsw, Class<?> resultType
761778
try {
762779
if (constructorMapping.getNestedQueryId() != null) {
763780
value = getNestedQueryConstructorValue(rsw.getResultSet(), constructorMapping, columnPrefix);
781+
} else if (JdbcType.CURSOR.equals(constructorMapping.getJdbcType())) {
782+
List<?> result = (List<?>) getNestedCursorValue(rsw.getResultSet(), constructorMapping, columnPrefix).get(0);
783+
if (objectFactory.isCollection(parameterType)) {
784+
MetaObject collection = configuration.newMetaObject(objectFactory.create(parameterType));
785+
collection.addAll((List<?>) result);
786+
value = collection.getOriginalObject();
787+
} else {
788+
value = toSingleObj(result);
789+
}
764790
} else if (constructorMapping.getNestedResultMapId() != null) {
765791
final String constructorColumnPrefix = getColumnPrefix(columnPrefix, constructorMapping);
766792
final ResultMap resultMap = resolveDiscriminatedResultMap(rsw.getResultSet(),
@@ -1527,10 +1553,19 @@ private void createRowKeyForMap(ResultSetWrapper rsw, CacheKey cacheKey) throws
15271553
}
15281554

15291555
private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue) {
1556+
linkObjects(metaObject, resultMapping, rowValue, false);
1557+
}
1558+
1559+
private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue,
1560+
boolean isNestedCursorResult) {
15301561
final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
15311562
if (collectionProperty != null) {
15321563
final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
1533-
targetMetaObject.add(rowValue);
1564+
if (isNestedCursorResult) {
1565+
targetMetaObject.addAll((List<?>) rowValue);
1566+
} else {
1567+
targetMetaObject.add(rowValue);
1568+
}
15341569

15351570
// it is possible for pending creations to get set via property mappings,
15361571
// keep track of these, so we can rebuild them.
@@ -1543,10 +1578,16 @@ private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Obj
15431578
pendingPccRelations.put(originalObject, pendingRelation);
15441579
}
15451580
} else {
1546-
metaObject.setValue(resultMapping.getProperty(), rowValue);
1581+
metaObject.setValue(resultMapping.getProperty(),
1582+
isNestedCursorResult ? toSingleObj((List<?>) rowValue) : rowValue);
15471583
}
15481584
}
15491585

1586+
private Object toSingleObj(List<?> list) {
1587+
// Even if there are multiple elements, silently returns the first one.
1588+
return list.isEmpty() ? null : list.get(0);
1589+
}
1590+
15501591
private Object instantiateCollectionPropertyIfAppropriate(ResultMapping resultMapping, MetaObject metaObject) {
15511592
final String propertyName = resultMapping.getProperty();
15521593
Object propertyValue = metaObject.getValue(propertyName);

src/main/java/org/apache/ibatis/mapping/MappedStatement.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2024 the original author or authors.
2+
* Copyright 2009-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

src/main/java/org/apache/ibatis/mapping/ResultMap.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Set;
2424

2525
import org.apache.ibatis.session.Configuration;
26+
import org.apache.ibatis.type.JdbcType;
2627

2728
/**
2829
* @author Clinton Begin
@@ -85,9 +86,8 @@ public ResultMap build() {
8586

8687
for (ResultMapping resultMapping : resultMap.resultMappings) {
8788
resultMap.hasNestedQueries = resultMap.hasNestedQueries || resultMapping.getNestedQueryId() != null;
88-
resultMap.hasNestedResultMaps = resultMap.hasNestedResultMaps
89-
|| resultMapping.getNestedResultMapId() != null && resultMapping.getResultSet() == null;
90-
89+
resultMap.hasNestedResultMaps = resultMap.hasNestedResultMaps || resultMapping.getNestedResultMapId() != null
90+
&& resultMapping.getResultSet() == null && !JdbcType.CURSOR.equals(resultMapping.getJdbcType());
9191
final String column = resultMapping.getColumn();
9292
if (column != null) {
9393
resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));

src/site/es/xdoc/sqlmap-xml.xml

+48
Original file line numberDiff line numberDiff line change
@@ -1428,6 +1428,31 @@ When using this functionality, it is preferable for the entire mapping hierarchy
14281428
columnPrefix="co_" />
14291429
</resultMap>]]></source>
14301430

1431+
1432+
<h4>Nested Cursor for Association</h4>
1433+
1434+
<p>Some databases can return <code>java.sql.ResultSet</code> as a column value.<br />
1435+
Here is the statement and result map.</p>
1436+
1437+
<source><![CDATA[<resultMap id="blogResult" type="Blog">
1438+
<id property="id" column="id" />
1439+
<result property="title" column="title" />
1440+
<association property="author" column="author" jdbcType="CURSOR">
1441+
<id property="id" column="id" />
1442+
<result property="username" column="username" />
1443+
</association>
1444+
</resultMap>
1445+
1446+
<select id="selectBlog" resultMap="blogResult">
1447+
select b.id, b.name, cursor(
1448+
select a.id, a.username from author a where b.author_id = a.id
1449+
) author from blog b
1450+
</select>]]></source>
1451+
1452+
<p>Compared to the examples in the previous section, the key difference is the <code>jdbcType</code> attribute in the <code>&lt;association&gt;</code> element.<br />
1453+
Its value <code>CURSOR</code> indicates that the value of the column <code>author</code> is nested cursor.</p>
1454+
1455+
14311456
<h4>ResultSets múltiples en Association</h4>
14321457

14331458
<table>
@@ -1601,6 +1626,29 @@ SELECT * FROM AUTHOR WHERE ID = #{id}]]></source>
16011626
<result property="body" column="body"/>
16021627
</resultMap>]]></source>
16031628

1629+
1630+
<h4>Nested Cursor for Collection</h4>
1631+
1632+
<p>It might be obvious, but nested cursor can return multiple rows.<br />
1633+
Just like <code>&lt;association&gt;</code>, you just need to specify <code>jdbcType="CURSOR"</code> in the <code>&lt;collection&gt;</code> element.</p>
1634+
1635+
<source><![CDATA[<resultMap id="blogResult" type="Blog">
1636+
<id property="id" column="id" />
1637+
<result property="title" column="title" />
1638+
<collection property="posts" column="posts" jdbcType="CURSOR">
1639+
<id property="id" column="id" />
1640+
<result property="subject" column="subject" />
1641+
<result property="body" column="body" />
1642+
</collection>
1643+
</resultMap>
1644+
1645+
<select id="selectBlog" resultMap="blogResult">
1646+
select b.id, b.name, cursor(
1647+
select p.id, p.subject, p.body from post p where p.blog_id = b.id
1648+
) posts from blog b
1649+
</select>]]></source>
1650+
1651+
16041652
<h4>ResultSets múltiples en Collection</h4>
16051653

16061654
<p>

src/site/ja/xdoc/sqlmap-xml.xml

+48
Original file line numberDiff line numberDiff line change
@@ -1608,6 +1608,31 @@ User{username=Peter, roles=[Users, Maintainers, Approvers]}
16081608
columnPrefix="co_" />
16091609
</resultMap>]]></source>
16101610

1611+
1612+
<h4>ネストされたカーソルを association にマッピングする</h4>
1613+
1614+
<p>データベースによっては列の値として <code>java.sql.ResultSet</code> 返すことができます。<br />
1615+
このような結果をマッピングする例を説明します。</p>
1616+
1617+
<source><![CDATA[<resultMap id="blogResult" type="Blog">
1618+
<id property="id" column="id" />
1619+
<result property="title" column="title" />
1620+
<association property="author" column="author" jdbcType="CURSOR">
1621+
<id property="id" column="id" />
1622+
<result property="username" column="username" />
1623+
</association>
1624+
</resultMap>
1625+
1626+
<select id="selectBlog" resultMap="blogResult">
1627+
select b.id, b.name, cursor(
1628+
select a.id, a.username from author a where b.author_id = a.id
1629+
) author from blog b
1630+
</select>]]></source>
1631+
1632+
<p>上の章の例との重要な違いは、<code>&lt;association&gt;</code> 要素の <code>jdbcType</code> 属性に <code>CURSOR</code> を指定している点です。<br />
1633+
これによって、<code>author</code> 列の値をネストされたカーソルとしてマッピングすることができます。</p>
1634+
1635+
16111636
<h4>複数の ResultSet を association にマッピングする</h4>
16121637

16131638
<table>
@@ -1789,6 +1814,29 @@ SELECT * FROM AUTHOR WHERE ID = #{id}]]></source>
17891814
<result property="body" column="body"/>
17901815
</resultMap>]]></source>
17911816

1817+
1818+
<h4>ネストされたカーソルを collection にマッピングする</h4>
1819+
1820+
<p>当然ですが、ネストされたカーソルが複数の値を返す場合もあります。<br />
1821+
先に説明した <code>&lt;association&gt;</code> の場合と同様、 <code>&lt;collection&gt;</code> 要素に <code>jdbcType="CURSOR"</code> を指定してください。</p>
1822+
1823+
<source><![CDATA[<resultMap id="blogResult" type="Blog">
1824+
<id property="id" column="id" />
1825+
<result property="title" column="title" />
1826+
<collection property="posts" column="posts" jdbcType="CURSOR">
1827+
<id property="id" column="id" />
1828+
<result property="subject" column="subject" />
1829+
<result property="body" column="body" />
1830+
</collection>
1831+
</resultMap>
1832+
1833+
<select id="selectBlog" resultMap="blogResult">
1834+
select b.id, b.name, cursor(
1835+
select p.id, p.subject, p.body from post p where p.blog_id = b.id
1836+
) posts from blog b
1837+
</select>]]></source>
1838+
1839+
17921840
<h4>複数の ResultSets を collection にマッピングする</h4>
17931841

17941842
<p>

0 commit comments

Comments
 (0)