org.eclipse.persistence.queries.ScrollableCursor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.queries;
import java.util.*;
import java.sql.*;
import org.eclipse.persistence.exceptions.*;
import org.eclipse.persistence.internal.helper.InvalidObject;
import org.eclipse.persistence.internal.queries.JoinedAttributeManager;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.databaseaccess.*;
public class ScrollableCursor extends Cursor implements ListIterator {
protected transient Object nextObject;
protected transient Object previousObject;
/** Store the previous row, for 1-m joining. */
protected AbstractRecord previousRow;
/** Internal flag indicating if the end of the cursor has been reached */
protected boolean atEndOfCursor = false;
/**
* INTERNAL:
* Default constructor.
*/
public ScrollableCursor() {
super();
}
/**
* INTERNAL:
* constructor.
*/
public ScrollableCursor(DatabaseCall call, ScrollableCursorPolicy policy) {
super(call, policy);
setPosition(-1);
}
/**
* PUBLIC:
* Moves the cursor to the given row number in the result set
*/
public boolean absolute(int rows) throws DatabaseException {
clearNextAndPrevious();
try {
boolean suceeded = false;
int initiallyConforming = this.objectCollection.size();
if ((rows >= 0) && (rows <= initiallyConforming)) {
this.resultSet.beforeFirst();
setPosition(rows);
return true;
} else if (rows > initiallyConforming) {
suceeded = this.resultSet.absolute(rows - initiallyConforming);
if (suceeded) {
setPosition(initiallyConforming + rows);
} else {
// Must be afterLast.
setPosition(size() + 1);
}
return suceeded;
} else {// (rows < 0)
// Need to know how big the result set is...
return absolute(size() + rows);
}
} catch (SQLException exception) {
DatabaseException commException = getAccessor().processExceptionForCommError(this.session, exception, null);
if (commException != null) throw commException;
throw DatabaseException.sqlException(exception, getAccessor(), this.session, false);
}
}
/**
* PUBLIC:
* Add is not support for scrollable cursors.
*/
@Override
public void add(Object object) throws QueryException {
QueryException.invalidOperation("add");
}
/**
* PUBLIC:
* Moves the cursor to the end of the result set, just after the last row.
*/
public void afterLast() throws DatabaseException {
clearNextAndPrevious();
try {
this.resultSet.afterLast();
setPosition(size() + 1);
} catch (SQLException exception) {
DatabaseException commException = getAccessor().processExceptionForCommError(this.session, exception, null);
if (commException != null) throw commException;
throw DatabaseException.sqlException(exception, getAccessor(), this.session, false);
}
}
/**
* PUBLIC:
* Moves the cursor to the front of the result set, just before the first row
*/
public void beforeFirst() throws DatabaseException {
clearNextAndPrevious();
try {
this.resultSet.beforeFirst();
setPosition(0);
} catch (SQLException exception) {
DatabaseException commException = getAccessor().processExceptionForCommError(this.session, exception, null);
if (commException != null) throw commException;
throw DatabaseException.sqlException(exception, getAccessor(), this.session, false);
}
}
/**
* INTERNAL:
* Clear the cached next and previous object and row values.
* This must be called whenever the cursor is re-positioned.
*/
protected void clearNextAndPrevious() {
this.nextObject = null;
this.previousObject = null;
this.nextRow = null;
this.previousRow = null;
this.atEndOfCursor = false;
}
/**
* INTERNAL:
* Clear only the cached next and previous object values.
* Called by previous() and next() to maintain the cached next
* and previous row values.
*/
protected void clearNextAndPreviousObject() {
this.nextObject = null;
this.previousObject = null;
}
/**
* PUBLIC:
* Retrieves the current row index number
*/
public int currentIndex() throws DatabaseException {
return getPosition();
}
/**
* PUBLIC:
* Moves the cursor to the first row in the result set
*/
public boolean first() throws DatabaseException {
clearNextAndPrevious();
try {
if (this.objectCollection.size() > 0) {
setPosition(1);
this.resultSet.beforeFirst();
return true;
} else {
return this.resultSet.first();
}
} catch (SQLException exception) {
DatabaseException commException = getAccessor().processExceptionForCommError(this.session, exception, null);
if (commException != null) throw commException;
throw DatabaseException.sqlException(exception, getAccessor(), this.session, false);
}
}
/**
* INTERNAL:
* Retrieve the size of the open cursor by executing a count on the same query as the cursor.
*/
@Override
protected int getCursorSize() throws DatabaseException {
if (getKnownCursorSize() != -1) {
return getKnownCursorSize();
}
int currentPos = 0;
// If afterLast getRow() will return 0!
boolean wasAfterLast = false;
try {
wasAfterLast = this.resultSet.isAfterLast();
currentPos = this.resultSet.getRow();
this.resultSet.last();
} catch (SQLException exception) {
DatabaseException commException = getAccessor().processExceptionForCommError(this.session, exception, null);
if (commException != null) throw commException;
throw DatabaseException.sqlException(exception, getAccessor(), this.session, false);
}
int size = 0;
try {
size = this.resultSet.getRow();
if (wasAfterLast) {// Move the cursor back to where we were before calling last()
this.resultSet.afterLast();
} else if (currentPos == 0) {
this.resultSet.beforeFirst();
} else {
this.resultSet.absolute(currentPos);
}
} catch (SQLException exception) {
DatabaseException commException = getAccessor().processExceptionForCommError(this.session, exception, null);
if (commException != null) throw commException;
throw DatabaseException.sqlException(exception, getAccessor(), this.session, false);
}
return size;
}
protected int getKnownCursorSize() {
if (size == -1) {
return size;
} else {
return size - this.objectCollection.size();
}
}
protected Object getNextObject() {
return nextObject;
}
/**
* PUBLIC:
* Retrieves the current cursor position (current row). The first row is number 1, the second number 2, and so on.
* Unlike java.sql.ResultSet.getRow(), 0 is not returned if afterLast.
* Instead size() + 1 is returned.
* @return the current row number; 0 if there is no current row
* @exception DatabaseException if a database access error occurs
*/
@Override
public int getPosition() throws DatabaseException {
try {
if (this.position == -1) {
this.position = this.resultSet.getRow();
if (this.position == 0) {
// This could mean either beforeFirst or afterLast!
if (isAfterLast()) {
this.position = size() + 1;
}
} else {
this.position += this.objectCollection.size();
}
}
return this.position;
} catch (SQLException exception) {
DatabaseException commException = getAccessor().processExceptionForCommError(this.session, exception, null);
if (commException != null) throw commException;
throw DatabaseException.sqlException(exception, getAccessor(), this.session, false);
}
}
protected Object getPreviousObject() {
return previousObject;
}
/**
* PUBLIC:
* Indicates whether the cursor can move to the the next row
*/
@Override
public boolean hasMoreElements() throws DatabaseException {
return hasNext();
}
/**
* PUBLIC:
* Indicates whether the cursor can move to the the next row
*/
@Override
public boolean hasNext() throws DatabaseException {
if (isClosed()) {
return false;
}
loadNext();
return (this.nextObject != null);
}
/**
* PUBLIC:
* Indicates whether the cursor can move to the the next row
*/
public boolean hasNextElement() throws DatabaseException {
return hasNext();
}
/**
* PUBLIC:
* Indicates whether the cursor can move to the the previous row
*/
@Override
public boolean hasPrevious() throws DatabaseException {
if (isClosed()) {
return false;
}
loadPrevious();
return (getPreviousObject() != null);
}
/**
* PUBLIC:
* Indicates whether the cursor is after the last row in the result set.
*/
public boolean isAfterLast() throws DatabaseException {
try {
if (this.nextObject != null) {
return false;
}
if ((this.objectCollection.size() > 0) && (getPosition() <= this.objectCollection.size())) {
return false;
}
return this.resultSet.isAfterLast();
} catch (SQLException exception) {
DatabaseException commException = getAccessor().processExceptionForCommError(this.session, exception, null);
if (commException != null) throw commException;
throw DatabaseException.sqlException(exception, getAccessor(), this.session, false);
}
}
/**
* PUBLIC:
* Indicates whether the cursor is before the first row in the result set.
*/
public boolean isBeforeFirst() throws DatabaseException {
if (getPreviousObject() != null) {
return false;
}
return getPosition() == 0;
}
/**
* PUBLIC:
* Indicates whether the cursor is on the first row of the result set.
*/
public boolean isFirst() throws DatabaseException {
if (getPreviousObject() != null) {
return false;
}
try {
if (this.objectCollection.size() > 0) {
return getPosition() == 1;
}
return this.resultSet.isFirst();
} catch (SQLException exception) {
DatabaseException commException = getAccessor().processExceptionForCommError(this.session, exception, null);
if (commException != null) throw commException;
throw DatabaseException.sqlException(exception, getAccessor(), this.session, false);
}
}
/**
* PUBLIC:
* Indicates whether the cursor is on the last row of the result set.
*/
public boolean isLast() throws DatabaseException {
if (this.nextObject != null) {
return false;
}
try {
return this.resultSet.isLast();
} catch (UnsupportedOperationException ex) {
// isLast() is not supported by some drivers (specifically JConnect5.0)
// Do this the hard way instead.
try {
return this.resultSet.getRow() == getCursorSize();
} catch (SQLException ex2) {
DatabaseException commException = getAccessor().processExceptionForCommError(this.session, ex2, null);
if (commException != null) throw commException;
throw DatabaseException.sqlException(ex2, getAccessor(), this.session, false);
}
} catch (SQLException exception) {
DatabaseException commException = getAccessor().processExceptionForCommError(this.session, exception, null);
if (commException != null) throw commException;
throw DatabaseException.sqlException(exception, getAccessor(), this.session, false);
}
}
/**
* PUBLIC:
* Moves the cursor to the last row in the result set
*/
public boolean last() throws DatabaseException {
clearNextAndPrevious();
try {
boolean isLast = this.resultSet.last();
if (!isLast) {
// cursor must be empty.
if (this.objectCollection.size() > 0) {
setPosition(this.objectCollection.size());
isLast = true;
}
} else {
setSize(this.objectCollection.size() + this.resultSet.getRow());
setPosition(size);
}
return isLast;
} catch (SQLException exception) {
DatabaseException commException = getAccessor().processExceptionForCommError(this.session, exception, null);
if (commException != null) throw commException;
throw DatabaseException.sqlException(exception, getAccessor(), this.session, false);
}
}
/**
* Load the next object
*/
protected void loadNext() {
if (this.nextObject == null) {
Object next = retrieveNextObject();
this.nextObject = next;
}
}
/**
* Load the previous object. This is used solely for scrollable cursor support
*/
protected void loadPrevious() {
// CR#4139
if (this.previousObject == null) {
this.previousObject = retrievePreviousObject();
}
}
/**
* PUBLIC:
* This method differs slightly from conventional read() operation on a Java stream. This
* method return the next object in the collection rather than specifying the number of
* bytes to be read in.
*
* Return the next object from the collection, if beyond the read limit read from the cursor
* @return - next object in stream
* @throws DatabaseException if read pass end of stream
*/
@Override
public Object next() throws DatabaseException, QueryException {
loadNext();
if (this.nextObject == null) {
throw QueryException.readBeyondStream(this.query);
}
Object next = this.nextObject;
clearNextAndPreviousObject();
return next;
}
/**
* PUBLIC:
* This method differs slightly from conventional read() operation on a Java stream. This
* method returns the next number of objects in the collection in a vector.
*
* Return the next specified number of objects from the collection, if beyond the read limit read from the cursor
* @param number - number of objects to be returned
* @return - vector containing next number of objects
* @throws DatabaseException if read pass end of stream
*/
public List