如何让 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个学期成绩


<<:  Day24:【技术篇】设定自己的GitHub Pages

>>:  【Day22-图表】文不如表,表不如图——使用seaborn一行透过图表观察资料!

Day13 AR的处理器发展如何 让我们继续|看|下|去

本篇要介绍AR的一些处理器(晶片)。 在AR和VR头戴装置上,主要通用CPU是高通(Qualcomm...

AI ninja project [day 16] 文字处理 -- 回归

我们已经有了语音转文字的技术, 那我们也能将文字进行向量化。 那我们是否能收集客服人员顾客的回答, ...

Day3-阿~ 那残忍的一巴掌(Nonce取得)

讨生活的台风补班日-偷偷用Chrome来唤醒一下头顶的灯泡 --------------------...

Day14 用 100 寸超大萤幕写 Code 的感觉 - 用 metatable 改变预设行为

前两天我已经学会用 CC: Tweaked 电脑读取磁片和播放音乐 今天我要来写 Code 啦 !!...

Day28 跟着官方文件学习Laravel-cache

当我们在读取DB资料时可能会占用大量 CPU 的资源让请求需要花几秒钟完成,这种情况我们会使用缓存,...