2010-01-20 6 views
14

У меня есть набор строк в базе данных, и я хотел бы предоставить интерфейс для раскрутки через них, как это:Scala: Разоблачение JDBC ResultSet через генератор (итерация)

def findAll: Iterable[MyObject] 

Там, где мы не требуем одновременного использования всех экземпляров в памяти. В C# вы можете легко создавать генераторы, подобные этому с использованием yield, компилятор позаботится о преобразовании кода, который перебирает набор записей в итератор (вроде его инвертирования).

Мой текущий код выглядит следующим образом:

def findAll: List[MyObject] = { 
    val rs = getRs 
    val values = new ListBuffer[MyObject] 
    while (rs.next()) 
    values += new valueFromResultSet(rs) 
    values.toList 
} 

Есть ли способ, которым я мог бы преобразовать это не хранить весь набор в памяти? Возможно, я мог бы использовать для понимания?

ответ

13

Попробуйте расширить Итератор вместо этого. Я не проверял, но что-то вроде этого:

def findAll: Iterator[MyObject] = new Iterator[MyObject] { 
    val rs = getRs 
    override def hasNext = rs.hasNext 
    override def next = new valueFromResultSet(rs.next) 
} 

Это должно хранить RS, когда это называется, а иначе просто быть легкой оберткой для звонков в Rs.

Если вы хотите сохранить значения, которые вы проходите, проверьте Stream.

+0

Я дам этот снимок, thx Rex –

+0

Я просто предположил, что rs на самом деле имеет метод hasNext. Если нет, вы должны кэшировать следующий результат с помощью (вероятно, частного) var внутри итератора и иметь hasNext, чтобы сказать, существует ли этот кешированный результат. –

+1

Да, это работает, я использовал hasNext = rs.isLast. Одна проблема заключается в том, что у меня нет механизма для закрытия rs и соединения. В моем текущем коде я завернул (выше) код в методе «использования», который закрывает для меня вещи. –

12

я наткнулся на одной и той же проблемы и на основе идей выше, я создал следующее решение, просто написав класс адаптера:

class RsIterator(rs: ResultSet) extends Iterator[ResultSet] { 
    def hasNext: Boolean = rs.next() 
    def next(): ResultSet = rs 
} 

При этом вы можете, например, выполнять карту операции на наборе результатов - который был моим личным намерением:

val x = new RsIterator(resultSet).map(x => { 
    (x.getString("column1"), x.getInt("column2")) 
}) 

Добавляем .toList для того, чтобы заставить оценку. Это полезно, если соединение с базой данных закрыто перед использованием значений. В противном случае вы получите сообщение об ошибке, когда вы не сможете получить доступ к ResultSet после закрытия соединения.

+0

Я использовал это решение. Большое спасибо! Мне нужно было вызвать toList, чтобы использовать результаты. val externalKeys = resultSet.map {x => x.getString ("external_key") } .toList – JasonG

+0

Существует проблема с этим. В соответствии со спецификацией hasNext он должен выполняться без продвижения набора результатов.Следовательно, лучше использовать кеширование, предложенное @Rex Kerr, или использование isLast (не уверен в производительности). – Sohaib

6

Более простой (идиоматический) способ добиться того же будет

Iterator.continually((rs.next(), rs)).takeWhile(_._1).map(r => valueFromResultSet(r._2)).toList 

Вам нужно .toList, чтобы заставить оценки, в противном случае базовая коллекция будет потоком и ResultSet может быть закрыт до оценки имеет место ,