Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,12 @@
<version>9.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@harawata Don't you want the java 11 version here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch 👍
I'll update later.

<version>23.6.0.24.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
Expand Down Expand Up @@ -254,6 +260,12 @@
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>oracle-free</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
<!-- For javadoc link -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,34 +247,49 @@ public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException
}

private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
ResultSet rs = stmt.getResultSet();
while (rs == null) {
// move forward to get the first resultset in case the driver
// doesn't return the resultset as the first result (HSQLDB)
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else if (stmt.getUpdateCount() == -1) {
// no more results. Must be no resultset
break;
ResultSet rs = null;
SQLException e1 = null;

try {
rs = stmt.getResultSet();
} catch (SQLException e) {
// Oracle throws ORA-17283 for implicit cursor
e1 = e;
}

try {
while (rs == null) {
// move forward to get the first resultset in case the driver
// doesn't return the resultset as the first result (HSQLDB)
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else if (stmt.getUpdateCount() == -1) {
// no more results. Must be no resultset
break;
}
}
} catch (SQLException e) {
throw e1 != null ? e1 : e;
}

return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}

private ResultSetWrapper getNextResultSet(Statement stmt) {
// Making this method tolerant of bad JDBC drivers
try {
if (stmt.getConnection().getMetaData().supportsMultipleResultSets()) {
// Crazy Standard JDBC way of determining if there are more results
// DO NOT try to 'improve' the condition even if IDE tells you to!
// It's important that getUpdateCount() is called here.
if (!(!stmt.getMoreResults() && stmt.getUpdateCount() == -1)) {
ResultSet rs = stmt.getResultSet();
if (rs == null) {
return getNextResultSet(stmt);
} else {
return new ResultSetWrapper(rs, configuration);
}
// We stopped checking DatabaseMetaData#supportsMultipleResultSets()
// because Oracle driver (incorrectly) returns false

// Crazy Standard JDBC way of determining if there are more results
// DO NOT try to 'improve' the condition even if IDE tells you to!
// It's important that getUpdateCount() is called here.
if (!(!stmt.getMoreResults() && stmt.getUpdateCount() == -1)) {
ResultSet rs = stmt.getResultSet();
if (rs == null) {
return getNextResultSet(stmt);
} else {
return new ResultSetWrapper(rs, configuration);
}
}
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2024 the original author or authors.
* Copyright 2009-2025 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.
Expand Down Expand Up @@ -92,9 +92,6 @@ void shouldRetainColumnNameCase() throws Exception {
when(rsmd.getColumnLabel(1)).thenReturn("CoLuMn1");
when(rsmd.getColumnType(1)).thenReturn(Types.INTEGER);
when(rsmd.getColumnClassName(1)).thenReturn(Integer.class.getCanonicalName());
when(stmt.getConnection()).thenReturn(conn);
when(conn.getMetaData()).thenReturn(dbmd);
when(dbmd.supportsMultipleResultSets()).thenReturn(false); // for simplicity.

final List<Object> results = fastResultSetHandler.handleResultSets(stmt);
assertEquals(1, results.size());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2009-2025 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
*
* https://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.submitted.oracle_implicit_cursor;

import java.util.List;
import java.util.Objects;

public class Author {
private Integer id;
private String name;
private List<Book> books;

public Author() {
super();
}

public Author(Integer id, String name, List<Book> books) {
super();
this.id = id;
this.name = name;
this.books = books;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<Book> getBooks() {
return books;
}

public void setBooks(List<Book> books) {
this.books = books;
}

@Override
public int hashCode() {
return Objects.hash(books, id, name);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Author)) {
return false;
}
Author other = (Author) obj;
return Objects.equals(books, other.books) && Objects.equals(id, other.id) && Objects.equals(name, other.name);
}

@Override
public String toString() {
return "Author [id=" + id + ", name=" + name + ", books=" + books + "]";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 2009-2025 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
*
* https://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.submitted.oracle_implicit_cursor;

import java.util.Objects;

public class Book {
private Integer id;
private String name;

public Book() {
super();
}

public Book(Integer id, String name) {
super();
this.id = id;
this.name = name;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public int hashCode() {
return Objects.hash(id, name);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Book)) {
return false;
}
Book other = (Book) obj;
return Objects.equals(id, other.id) && Objects.equals(name, other.name);
}

@Override
public String toString() {
return "Book [id=" + id + ", name=" + name + "]";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2009-2025 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
*
* https://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.submitted.oracle_implicit_cursor;

import java.util.List;

public interface Mapper {

List<Author> selectImplicitCursors_Statement();

List<Author> selectImplicitCursors_Prepared();

List<Author> selectImplicitCursors_Callable();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2009-2025 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
*
* https://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.submitted.oracle_implicit_cursor;

import static org.junit.jupiter.api.Assertions.assertIterableEquals;

import java.util.List;
import java.util.function.Function;

import org.apache.ibatis.BaseDataTest;
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.testcontainers.OracleTestContainer;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag("TestcontainersTests")
class OracleImplicitCursorTest {

private static SqlSessionFactory sqlSessionFactory;

@BeforeAll
static void setUp() throws Exception {
Configuration configuration = new Configuration();
Environment environment = new Environment("development", new JdbcTransactionFactory(),
OracleTestContainer.getUnpooledDataSource());
configuration.setEnvironment(environment);
configuration.addMapper(Mapper.class);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
"org/apache/ibatis/submitted/oracle_implicit_cursor/CreateDB.sql");
}

@Test
void shouldImplicitCursors_Statement() {
doTest(Mapper::selectImplicitCursors_Statement);
}

@Test
void shouldImplicitCursors_Prepared() {
doTest(Mapper::selectImplicitCursors_Prepared);
}

@Test
void shouldImplicitCursors_Callable() {
doTest(Mapper::selectImplicitCursors_Callable);
}

private void doTest(Function<Mapper, List<Author>> query) {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
Mapper mapper = sqlSession.getMapper(Mapper.class);
List<Author> authors = query.apply(mapper);
assertIterableEquals(
List.of(new Author(1, "John", List.of(new Book(1, "C#"), new Book(2, "Python"), new Book(5, "Ruby"))),
new Author(2, "Jane", List.of(new Book(3, "SQL"), new Book(4, "Java")))),
authors);
}
}
}
Loading
Loading