iT邦幫忙

2021 iThome 鐵人賽

DAY 22
0
Modern Web

使用 Kotlin 快速開發 Web 程式 -- Vaadin系列 第 22

如何讓 Grid 顯示關聯式 SQL 查詢的資料 - day22

承前例,接著要完成選取學期後 grid 動態查詢更新

https://ithelp.ithome.com.tw/upload/images/20211007/20138680GwvpUv1wXY.png

vok-orm 資料加載

前端顯示多筆資料內容像是list、recordset時,一般會使用table(html)、grid這一類元件來顯示。通常這類表格會提供排序和過濾功能,別忘了我們寫的是網頁程式,資料是由後端提供給前端顯示,在提供資料給前端顯示時,一次資料量不宜過多,通常以頁為單位提供資料。

vok-ork 提供的 Data Loaders能達成上述所提到的 sorting、filtering、lazy-loading等功能。vok-ork 提供兩種易於使用的data loaders EntityDataLoaderSqlDataLoader

  • 前面範例裡顯示學生資料即是EntityDataLoader
    setDataLoader(Student.dataLoader)
  • 在接下來的例子中,將會使用到SqlDataLoader

Grid 資料來源

  • List

預設情況下,Grid 綁定一個item list,使用setItems()綁定資料。例:

val students = listOf<Student>(Student(name = "A"), 
                               Student(name = "B"), 
                               Student(name = "C"))
    :
    :
grid.setItems(students)
  • DataLoader

例:

data class Student(
    override var id: Long? = null,
    var name: String? = null,
    var birthday: LocalDate? = null,
    var created: Date? = null,
    var gender: Gender? = null,
    var height: Double? = null,
    var weight: Double? = null,
    var photo: String? = null,
    var student_id: String? = null
) : KEntity<Long> {
    companion object : Dao<Student, Long>(Student::class.java)
}
    grid{
        setDataLoader(Student.dataLoader)
    }
  • DataProvider

官方文件中對於 Interface DataProvider說明如下

A common interface for fetching data from a backend. The DataProvider interface is used by listing components implementing HasDataProvider or HasFilterableDataProvider. The listing component will provide a Query object with request information, and the data provider uses this information to return a stream containing requested beans.

Vaadin comes with a ready-made solution for in-memory data, known as ListDataProvider which can be created using static create methods in this interface. For custom backends such as SQL, EntityManager, REST APIs or SpringData, use a BackEndDataProvider or its subclass.

如前所述,在網頁程式裡,前端資料是由後端提供給前端顯示,grid 動態更改資料來源,不能直接將 List 指定給setItems(),必需使用ListDataProvider。接下來的範例中,我們將會使用ListDataProvider

查詢符合條件成績資料,並排序

  1. 設定 Bean

此為查詢結果要映射的Bean類,要取得的欄位除了學生成績,還有平均、學生姓名。這個bean類資料要從後端傳到前端,所以必需可序列化,請記得加上 Serializable

data class StudentGrade(
    var description: String? = null,
    var english: Double? = null,
    var math: Double? = null,
    var mandarin: Double? = null,
    var pe: Double? = null,
    var student_id: Long? = null,
    var name: String? = null,
    var avg: Double? = null
) : Serializable
  1. 取得學生成績

vok-orm 所提供的 SqlDataLoader所提供的SQL中有幾個比較特別的{{WHERE}}{{ORDER}}{{PAGING}},都是預留空間。當指定條件時會進行替代。


companion object {
    val sql = "select grade.*,round((grade.english + grade.math + grade.mandarin + grade.pe)/4,2) as avg, student.name from grade, student where grade.student_id = student.id {{WHERE}} order by 1=1{{ORDER}} {{PAGING}}"
        
    fun getListDataProvider(semester: String?): ListDataProvider<StudentGrade> {
        val filter = buildFilter<StudentGrade> { 
            "grade.description = :semester"("semester" to semester) 
        }
        val provider = SqlDataLoader(DaoOfAny(StudentGrade::class.java), sql)
        val result: List<StudentGrade> = 
            provider.fetch(filter, sortBy = listOf("avg".desc))
        
        return DataProvider.ofCollection(result)
    }
}

在此例中,若semester = "第 1 學期",最終 sql 為

select grade.*,round((grade.english + grade.math + grade.mandarin + grade.pe)/4,2) as avg, student.name from grade, student where grade.student_id = student.id and grade.description = `第 1 學期` order by 1=1 , avg desc

對於SQL語法熟悉的開發者而言,應該非常樂見 SqlDataLoader

  1. 設定 Grid 顯示欄位

    grid = grid {
        isExpand = true
        setSizeFull()
        dataProvider = StudentGrade.getListDataProvider(null)
        addColumn(StudentGrade::name).setHeader("姓名")
        addColumn(StudentGrade::description).setHeader("學期")
        addColumn(StudentGrade::english).setHeader("英文")
        addColumn(StudentGrade::mandarin).setHeader("國文")
        addColumn(StudentGrade::math).setHeader("數學")
        addColumn(StudentGrade::pe).setHeader("體育")
        addColumn(StudentGrade::avg).setHeader("平均")
    }
  1. 選取學期

使用 valueChangeListener 來傾聽選項是否改變,若有異動則更新 semester

    comboBox<String>("學期") {
        setItems(source)
        addValueChangeListener {
            semester = value
        }
    }
  1. 送出查詢、更新Grid

    button("QUERY") {
        onLeftClick {
            grid.dataProvider = StudentGrade.getListDataProvider(semester)
        }
    }

上述程式之測試資料為 10000 筆學生資料,每位學生5個學期成績


上一篇
vok-orm 自訂sql 查詢 / db connection - day21
下一篇
立委名單/提案 Open Data / CsvToBean - day23
系列文
使用 Kotlin 快速開發 Web 程式 -- Vaadin30

尚未有邦友留言

立即登入留言