2010-01-20 22 views
14

Tôi đã có một tập hợp các hàng trong một cơ sở dữ liệu, và tôi muốn cung cấp một giao diện để quay qua chúng như thế này:Scala: Phơi bày một JDBC ResultSet thông qua một máy phát điện (iterable)

def findAll: Iterable[MyObject] 

Nơi chúng tôi không yêu cầu phải có tất cả các cá thể trong bộ nhớ cùng một lúc. Trong C# bạn có thể dễ dàng tạo ra các máy phát điện như thế này bằng cách sử dụng năng suất, trình biên dịch sẽ chăm sóc chuyển đổi mã lặp qua recordset thành một trình lặp (loại đảo ngược nó).

mã hiện tại của tôi trông như thế này:

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

Có cách nào tôi có thể chuyển đổi này để không lưu trữ toàn bộ tập trong bộ nhớ? Có lẽ tôi có thể sử dụng để hiểu?

Trả lời

13

Hãy thử mở rộng Iterator thay thế. Tôi đã không thử nghiệm nó, nhưng một cái gì đó như thế này:

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

Điều này sẽ lưu trữ rs khi nó được gọi, và nếu không chỉ là một wrapper ánh sáng để gọi đến rs.

Nếu bạn muốn lưu các giá trị mà bạn đi qua, hãy xem Luồng.

+0

Tôi sẽ cung cấp cho rằng một shot, thx Rex –

+0

Tôi chỉ giả định rằng rs thực sự có một phương pháp "hasNext". Nếu không, bạn nên cache kết quả kế tiếp với một var (có thể là private) bên trong iterator và có hasNext nói liệu kết quả đã lưu trữ có tồn tại hay không. –

+1

Vâng, nó hoạt động, tôi đã sử dụng hasNext = rs.isLast. Một vấn đề là mặc dù tôi không có bất kỳ cơ chế nào để đóng rs và kết nối. Trong mã hiện tại của tôi, tôi đã bao bọc (ở trên) mã trong một phương pháp "sử dụng" mà đóng cửa mọi thứ cho tôi. –

12

tôi đi qua cùng một vấn đề và dựa trên những ý tưởng trên, chúng tôi tạo ra các giải pháp sau đây bằng cách đơn giản viết một lớp adapter:

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

Với điều này bạn có thể ví dụ thực hiện đồ hoạt động trên tập kết quả - đó là ý định của cá nhân tôi:

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

Nối một .toList để buộc đánh giá. Điều này rất hữu ích nếu kết nối cơ sở dữ liệu bị đóng trước khi bạn sử dụng các giá trị. Nếu không, bạn sẽ nhận được một lỗi nói rằng bạn không thể truy cập vào ResultSet sau khi kết nối được đóng lại.

+0

Tôi đã sử dụng giải pháp này. Cảm ơn bạn nhiều! Tôi đã phải gọi toList để sử dụng kết quả. val externalKeys = resultSet.map {x => x.getString ("external_key") } .toList – JasonG

+0

Có vấn đề với điều này. Theo đặc tả cho hasNext, nó sẽ thực hiện mà không cần tiến hành resultset.Do đó hoặc bộ nhớ đệm theo gợi ý của @Rex Kerr hoặc sử dụng isLast (không chắc chắn về hiệu suất) là một lựa chọn tốt hơn ở đó. – Sohaib

6

A (thành ngữ) cách đơn giản để đạt được cùng sẽ

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

Bạn cần .toList để buộc đánh giá, nếu không thì bộ sưu tập cơ bản sẽ là một dòng suối và ResultSet có thể đóng cửa trước khi đánh giá đã diễn ra .

Các vấn đề liên quan