QtQuick桌面应用程序开发指南 4)动态管理Note对象_B 5)加强外观 6)许多其他的改进

时间:2022-09-03 12:46:07

4.2.2 Stateless(不管状态)JavaScript库

为了让开发轻松点, 使用一个JavaScript接口来和数据库交互是个好主意, 它在QML中提供了方便的方法;

在QtCreator中创建一个新的JavaScript文件 noteDB.js, 保证选择了 State Library选项; 这样使得noteDB.js用起来像一个库, 提供了stateless的helper方法; 这样,在每一个QML组件 import noteDB.js以及使用它的时候, 就仅仅有一份实例被载入和使用; 这也保证了仅仅有一个全局变量用来存储数据库实例: _db;

Note Non-stateless(非状态无关的)JavaScript文件在导入一个QML组件的时候非常实用, 能够对那个组件运行操作, 全部的变量仅仅有在那份context(上下文)中有效; 每次import都会创建一个独立的JavaScript文件的实例;

noteDB.js应该提供以下的功能:

- 打开/创建一个本地数据库实例

- 创建必须的数据库table(表格)

- 从数据库读取notes

- 删除全部的notes

接下来会看到 关于noteDB.js中的方法怎样被实现的很多的细节--读取, 存储note item到数据库的实现; 先考虑以下的方法实现:

- function openDB() - 假设数据库不存在, 就创建一个, 存在就打开它;

- function createNoteTable() - 假设note table不存在就创建note table; 这种方法仅仅会在 openDB()中调用;

- function clearNoteTable() - 从note table中移除全部的行(row);

- readNotesFromPage(markerId) - 这个helper方法读取全部和特定markerId相关联的note, 返回一个dictionary(字典)类型的数据;

- function saveNotes(noteItems, markerId) - 用来在数据库中保存note item; noteItem參数代表一个note item的list(列表), markerId代表note item隶属的那个对应的page;

Note 全部这些JavaScript方法使用Qt Quick Local Storage API来获取数据库, 声明语句(statement) import QtQuick.LocalStorage 2.0 as Sql, 要写在 noteDB.js的開始;

4.2.3 读取和存储Note

实现了noteDB.js之后, 能够用法来读取和存储note了;

一个好的做法是在main.qml文件里, 在初始化的时候或打开数据库连接的时候才去调用; 这样我们能够使用定义在noteDB.js中的JavaScript方法而不会又一次初始化(reinitializing)数据库;

在main.qml里面导入noteDB.js 和 QtQuick.LocalStorage 2.0, 有个问题是什么时候调用 openDB()方法; QML提供了helpful attached signal: onCompleted()和 onDestruction(), 会在Component被全然载入全然销毁的时候各自emit(发送);

// main.qml

1
2
3
4
5
6
7
8
9
import
QtQuick 2.0
import "noteDB.js" as
NoteDB
 
Item
{
    //
this signal is emitted upon component loading completion
    Component.onCompleted:
{
        NoteDB.openDB()
    }
//...

以下是openDB方法实现, 它调用 openDatabaseSync()方法来创建数据库, 之后调用 createNoteTable()方法来创建note表;

//noteDB.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.pragma
library
.import
QtQuick.LocalStorage 2.0 as Sql
//
declaring a global variable for storing the database instance
var _db
 
function openDB()
{
    print("noteDB.createDB()")
    _db
= openDatabaseSync(
"StickyNotesDB","1.0",
            "The
stickynotes Database"
,
1000000);
    createNoteTable();
}
 
function createNoteTable()
{
    print("noteDB.createTable()")
    _db.transaction( function(tx)
{
        tx.executeSql(
            "CREATE
TABLE IF NOT EXISTS note
            (noteId
INTEGER PRIMARY KEY AUTOINCREMENT,
            x
INTEGER,
            y
INTEGER,
            noteText
TEXT,
            markerId
TEXT)"
)
    })
}

在main.qml里面, 初始化了数据库, 安全地開始从Page组件中载入Note item; 上面我们提到了 readNotesFromPage(markerId)方法, 返回一个data array(数组)list (參照script world--脚本世界里的dictionary), 每一个数组代表数据库中的一行--note的数据;

//noteDB.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//...
function readNotesFromPage(markerId)
{
    print("noteDB.readNotesFromPage()
"
 +
markerId)
    var noteItems
= {}
    _db.readTransaction( function(tx)
{
        var rs
= tx.executeSql(
            "SELECT
FROM note WHERE markerId=?
            ORDER
BY markerid DESC"
,
[markerId] );
        var item
        for (var i=0;
i< rs.rows.length; i++) {
            item
= rs.rows.item(i)
            noteItems[item.noteId]
= item;
        }
    })
    return noteItems
}

Page组件会读取note然后创建对应的QML note对象;

// Page.qml

1
2
3
4
5
6
7
8
9
10
11
//...
//
when the component is loaded, call the loadNotes()
//
function to load notes from the database
Component.onCompleted:
loadNotes()
//
a Javascript helper function that reads the note data from database
function loadNotes()
{
    var noteItems
= NoteDB.readNotesFromPage(markerId)
    for (var in noteItems)
{
        newNoteObject(noteItems[i])
    }
}

我们能够看到 newNoteObject()方法在之前的Page.qml定义, 它得到一个data数组作为參数, 实际上就是 x, y, noteText, markerId和 noteId属性的值;

Note 注意note表的name域(field)和Note组件的属性一样; 这能够帮助我们把table行的数据作为參数传递, 创建note QML对象;

如今我们实现了从数据库载入Note item的方法, 下一个逻辑步骤是实现一个将note存储到DB中的方法; PagePanel组件是负责创建Page item的, 因此它应该能够从每张page上读取note, 然后调用 saveNote() JS方法来存储note;

//noteDB.js

1
2
3
4
5
6
7
8
9
10
11
12
//...
function saveNotes(noteItems,
markerId) {
    for (var i=0;
i<noteItems.length; ++i) {
        var noteItem
= noteItems[i]
        _db.transaction( function(tx)
{
            tx.executeSql(
            "INSERT
INTO note (markerId, x, y, noteText)
            VALUES(?

,?

,?,?

)"

,
            [markerId,
noteItem.x, noteItem.y, noteItem.noteText]);
        })
    }
}

首先我们定义一个属性alias能够将Note item暴露出来, 这些item是作为在Page组件中container的children而创建的;

// Page.qml

1
2
3
4
5
//...
//
this property is used by the PagePanel component
//
for retrieving all the notes of a page and storing
//
them in the Database.
property
alias notes: container.children

在PagePanel中实如今DB中保存note的功能;

// PagePanel.qml

1
2
3
4
5
6
7
8
9
10
11
//...
    Component.onDestruction:
saveNotesToDB()
    //
a JavaScript function that saves all notes from the pages
    function saveNotesToDB()
{
        //
clearing the DB table before populating with new data
        NoteDB.clearNoteTable();
        //
storing notes for each individual page
        NoteDB.saveNotes(personalpage.notes,
personalpage.markerId)
        NoteDB.saveNotes(funpage.notes,
funpage.markerId)
        NoteDB.saveNotes(workpage.notes,
workpage.markerId)
    }

为了降低代码复杂度, 我们在保存note之前清楚DB中全部的数据; 这样能够不用谢代码来更新现存的Note item;

在结束本章前, 用户可创建和删除note, 程序也能够自己主动保存和载入note;

下一步

下一章介绍一些美丽的动画, 以及实现的步骤和技巧;

---4End---

CHAPTER5 外观加强

NoteApp的UI能够看作依照功能和用户交互实现的; 事实上还有非常大的进步空间, 让UI更吸引用户; QML设计成一种声明式语言, 记得用动画和UI的流畅过度;

这一章会一步步实现动画, 让NoteApp感觉更优美(fluid); QML提供了一组QML类型以各种方式实现动画; 这一章会介绍一些新的类型, 在QML组件中使用它们, 让UI更加流畅;

整体上, 本章覆盖以下几点:

- 介绍animation(动画)和transition(过渡)效果;

- 新的QML类型: Behavior, Transition, 各种Animation元素;

- 使用各种动画强化NoteApp的QML组件;

5.1 NoteToolbar动画

我们来看看怎样改进Note组件, 加入一些基于用户交互的行为; Note有一个toolbar, 能够用Delete tool删除笔记; toolbar是用来按下鼠标四处拖拽note用的;

一个改进是能够让Delete tool仅仅有在须要的时候可见; 比如, 在toolbar上悬停(hover)的时候让Delete工具可见, 使用 fade-in/fade-out(渐入渐出)效果更美丽;

Note opacity属性的值是从parent到child item传播的(propagated); [属性继承]

Behavior类型同意我们定义一个基于item的属性变化的行为;

// NoteToolbar.qml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//...   
    MouseArea
{
        id:
mousearea
        anchors.fill:
parent
        //
setting hoverEnabled property to true
        //
in order for the MouseArea to be able to get
        //
hover events
        hoverEnabled: true
    }
 
    //
using a Row element for laying out tool
    //
items to be added when using the NoteToolbar
    Row
{
        id:
layout
        layoutDirection:
Qt.RightToLeft
        anchors
{
            verticalCenter:
parent.verticalCenter;
            left:
parent.left;
            right:
parent.right
            leftMargin:
15;
            rightMargin:
15
        }
        spacing:
20
        //
the opacity depends if the mousearea
        //
has the cursor of the mouse.
        opacity:
mousearea.containsMouse ? 1 : 0
        //
using the behavior element to specify the
        //
behavior of the layout element
        //
when on the opacity changes.
        Behavior
on opacity {
            //
using NumberAnimation to animate
            //
the opacity value in a duration of 350 ms
            NumberAnimation
{ duration: 350 }
        }
    }
//...

上面代码能够看到, 我们启用MouseArea的hoverEnabled属性, 接收鼠标悬停事件; 之后, 我们能够把Row类型的opacity切换到0, 假设MouseArea类型没有悬停(hovered)就设为1; MouseArea的containsMouse属性用来决定opacity的值;

这样Behavior类型在Row中被创建出来, 基于opacity属性而定义它的行为; 当opacity改变时, NumberAnimation会被应用;

NumberAnimation类型应用基于一个数字值的改变, 我们对Row的opacity属性, 在350毫秒时长内显示它;

Note NumberAnimation类型是继承自PropertyAnimation, 它有一个 Easing.Linear作为 easing curve动画的默认值; [动画效果, 执行轨迹等...]

下一步

我们能够看到怎样使用Transition和其它QML Animation实现动画效果;

5.2 使用State和Transition

前面我们看到的定义简单的话的技巧是基于属性变化的, 使用了 Behavior和 NumberAnimation;

当然, 也有其它动画是依赖于一组属性变化的--能够用State实现;

如今来看看如何进一步实现NoteApp的UI;

Marker item看起来在用户交互的时候是静态的; 假设基于用户交互的不同场景给它加上一点动画会如何? 另外, 我们能够能够让当前激活的marker和当前的page对用户来说看起来更明明白;

5.2.1 Marker Items加上动画

我们差点儿相同能够总结一下在用户交互时改进Marker item的各种可能的场景, 下面是user case(用户用例)描写叙述:

- 当前激活的Marker item应该更加明显; 用户点击的时候, 一个marker要变成激活状态; 激活的marker会略微变大, 能够从左边向右边滑动一些;(像个抽屉)

- 当用户鼠标悬停在一个marker上面, marker从左到右滑动, 但不会像激活的marker一样滑动;

考虑上述场景, 我们须要在Marker和MarkerPanel组件上进行工作;

当独当上面关于行为需求的描写叙述(左到右的滑动效果), 马上出现的想法是改变 x的属性, 由于它代表了item在X-axis(轴)上的位置; 另外, 由于Marker item应该要知道它自己是否是当前激活的, 因此加入一个新的属性 active;

为Marker组件引入两个新的state, 代表上面描写叙述的行为:

- hovered: 当用户鼠标悬停的时候会更新marker的x属性值;

- selected: 当marker被激活, 即用户点击的时候, 会更新x属性值;

// Marker.qml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//
this property indicates whether this marker item
//
is the current active one. Initially it is set to false
property
bool active: 
false
 
//
creating the two states representing the respective
//
set of property changes
states:
[
    //
the hovered state is set when the user has
    //
the mouse hovering the marker item.
    State
{
        name: "hovered"
        //
this condition makes this state active
        when:
mouseArea.containsMouse && !root.active
        PropertyChanges
{ target: root; x: 5 }
    },
    State
{
        name: "selected"
        when:
root.active
        PropertyChanges
{ target: root; x: 20 }
    }
]
 
//
list of transitions that apply when the state changes
transitions:
[
    Transition
{
        to: "hovered"
        NumberAnimation
{ target: root; property: 
"x";
duration: 300 }
    },
    Transition
{
        to: "selected"
        NumberAnimation
{ target: root; property: 
"x";
duration: 300 }
    },
    Transition
{
        to: ""
        NumberAnimation
{ target: root; property: 
"x";
duration: 300 }
    }
 ]

因此我们声明两个state代表对应基于用户行为的属性变化; 每一个state绑定到when属性所描写叙述的情况中;

Note 对于MouseArea的containsMouse属性, hoverEnabled属性必须设为true;

Transition是用来在state之前切换时定义item的行为的; 我们能够在state激活的时候, 给变化的属性定义各种动画

Note item的默认state是一个空的string--("");

在MarkerPanel组件里, 我们在它被点击的时候必须设置avtive属性为true; 參考MarkerPanel.qml;

5.2.2 给PagePanel加入Transition

在PagePanel组件, 我们使用state来管理page之间的navigation(导航); 加入transition是自然的思路; 因为我们在每一个state里面改变了opacity属性, 能够加入给全部的state加入Transition来依据opacity的值执行NumberAnimation, 创建fade-in和fade-out效果;

// PagePanel.qml

1
2
3
4
5
6
7
8
9
10
//...
    //
creating a list of transitions for
    //
the different states of the PagePanel
    transitions:
[
    Transition
{
        //
run the same transition for all states
        from: "";
to: 
"*"
        NumberAnimation
{ property: 
"opacity";
duration: 500 }
    }
    ]

Note 一个item的opacity值也会被传播(propagated)到它的child元素;

下一步

进一步改进UI;

---5End---

CHAPTER6 很多其它改进

这一阶段, 能够觉得NoteApp的特性完毕, UI也符合需求; 只是, 这里还有非常大空间改进, 尽管不是最重要(minor)但能够使得程序更美丽, 以及准备部署(deployment);

这一章有些小的改进, 也有些新的主意和特性加入进来; 当然, 我们能够鼓舞继续开发NoteApp, 也许又一次设计整个UI, 引入很多其它特性;

这里有一个要点列表:

- 很多其它Javascript用来加入功能;

- 使用QML Item的z ordering(z轴次序);

- 使用自己定义的本地字体;

6.1 加强Note Item的功能

一个巧妙的(nifty)功能能够让note依据输入的text而变长; 为了简化, 当很多其它text输入时, 它会将text折叠(wrap)起来, 适应note宽度, 在垂直方向上将note变长;

Text类型有一个paintedHeight属性给出text在屏幕上绘制时的确切高度; 依据这个值, 我们能够添加或降低note自身的高度;

先定义一个Javascript helper方法, 为Item计算height属性的值, 它会放在Note组件的顶层;

// Note.qml

1
2
3
4
5
6
7
8
9
10
//
JavaScript helper function that calculates the height of
//
the note as more text is entered or removed.
function updateNoteHeight()
{
    //
a note should have a minimum height
    var noteMinHeight
= 200
    var currentHeight
= editArea.paintedHeight + toolbar.height +40
    root.height
= noteMinHeight
    if(currentHeight
>= noteMinHeight)
        root.height
= currentHeight
}

因为 updateNoteHeight()方法会依据editArea的paintedHeight属性更新root的height, 我们须要在paintedHeight变化的时候调用这种方法;

// Note.qml

1
2
3
4
5
6
7
8
TextEdit
{
    id:
editArea
    //...
    //
called when the painterHeight property changes
    //
then the note height has to be updated based
    //
on the text input
    onPaintedHeightChanged:
updateNoteHeight()
//...

Note 每一个属性有一个notifier signal(通知信号), 每次property改变的时候会被发送(emit);

updateNoteHeight() JS方法会改变height属性, 所以我们能够使用Behavior定义一个行为;

// Note.qml

1
2
3
4
//...
//
defining a behavior when the height property changes
//
for the root element
Behavior
on height { NumberAnimation {} }

下一步

展示怎样使用Item的z属性来正确地为note排序;

6.2 Note排序

Page里面的Note无法知道用户当前正在使用哪一个note; 默认情况, 全部创建出来的Note item都有一样的z属性默认值, 这样的情况下, QML为item创建默认的栈次序, 依赖于哪一个item先被创建;

所须要的行为应该是依据用户交互来改变note的次序; 当用户点击note toolbar, 或者開始编辑note的时候, 当前的note应该跑到前面而不是躲在其它note以下; 这能够通过改变z值来做到, z值比其它的note高就可以;

// Note.qml

1
2
3
//...
//
setting the z order to 1 if the text area has the focus
z:
editArea.activeFocus ?

1:0

activeFocus属性在editArea有输入焦点(input focus)的时候变为true; 因此让z属性依赖editArea的activeFocus会比較安全; 然后, 我们须要保证editArea在用户点击toolbar的时候有输入焦点; 我们在NoteToolbar组件中定义一个pressed()信号, 在鼠标press的时候发送;

// NoteToolbar.qml

1
2
3
4
5
6
7
8
9
10
11
//...
    //
this signal is emitted when the toolbar is pressed by the user
    signal
pressed()
 
    //
creating a MouseArea item
    MouseArea
{
        id:
mousearea
//...
        //
emitting the pressed() signal on a mouse press event
        onPressed:
root.pressed()
    }

在MouseArea中的onPressed信号handler中, 我们发送NoteToolbar(root)的pressed()信号;

NoteToolbar的pressed()信号在Note组件中会处理;

// Note.qml

1
2
3
4
5
6
    //
creating a NoteToolbar that will be anchored to its parent
    NoteToolbar
{
        id:
toolbar
        //
setting the focus on the text area when the toolbar is pressed
        onPressed:
editArea.focus = 
true
//...

上面代码中, 我们将editArea的focus属性设为true, editArea会接收到输入焦点; 这样activeFocus属性变为true, 触发(trigger)z属性值的变化;

下一步

如何载入以及使用一个自己定义的本地font(字体)文件;

6.3 载入自己定义字体

在现代程序中使用部署一个自己定义的font而不依赖系统font是非经常见的方式; 对于NoteApp, 我们想要用QML提供的方法做同样的事情;

FontLoader类型让你能够通过名字或URL路径来载入font; 由于载入的font能够被整个程序广泛使用, 我们建议在main.qml中载入它, 然后在其它组件中使用;

// main.qml

1
2
3
4
5
6
7
8
9
10
11
//...
    //
creating a webfont property that holds the font
    //
loading using FontLoader
    property var webfont:
FontLoader {
        id:
webfontloader
        source: "fonts/juleeregular.ttf"
        onStatusChanged:
{
        if (webfontloader.status
== FontLoader.Ready)
            console.log("font----Loaded")
        }
    }

这样我们已经为window item创建了一个webfont属性; 这个属性能够安全地在其它的组件中使用, 能够在editArea的Note组件中使用;

// Note.qml

1
2
3
4
    TextEdit
{
        id:
editArea
        font.family:
window.webfont.name; font.pointSize: 13
//...

要设置editArea的font, 使用font.family属性; 通过window, 我们使用它的webfont属性来获取font名字, 用来设置;

下一步

将NoteApp准备好部署的细节;

---6End---

---TBC---YCR---

QtQuick桌面应用程序开发指南 4)动态管理Note对象_B 5)加强外观 6)许多其他的改进的更多相关文章

  1. QtQuick桌面应用程序开发指导 3&rpar;达到UI而功能&lowbar;B 4&rpar;动态管理Note物&lowbar;A

    3.2 把Page Item和Marker Item绑定 之前我们实现了PagePanel组件, 使用了三个state来切换Page组件的opacity属性; 这一步我们会使用Marker和Marke ...

  2. python 整型--《Python 3程序开发指南》笔记

    参考:<Python 3程序开发指南> 整数转换函数: bin(i) 返回整数i的二进制表示(字符串) hex(i) 返回i的十六进制表示(字符串) int(x) 将x转换为整数,失败产生 ...

  3. CreateWindowEx failed &lpar;当前程序已使用了 Window 管理器对象的系统允许的所有句柄。&rpar;

    我在QT图形场景视图中通过QGraphicsProxyWidget添加代理Widget(实现添加基本的QT Widget,如按钮.复选框.日期时间控件等),当数量超过3500左右的时候,QT应用程序直 ...

  4. freeMarker(八)——程序开发指南之配置(Configuration)

    学习笔记,选自freeMarker中文文档,译自 Email: ddekany at users.sourceforge.net 1.基本内容 配置(configuration)就是 freemark ...

  5. freeMarker(六)——程序开发指南入门

    学习笔记,选自freeMarker中文文档,译自 Email: ddekany at users.sourceforge.net 1.创建Configuration实例 首先,你应该创建一个 free ...

  6. Electron与WEB桌面应用程序开发及其它

    这几天在构思项目,研究了一下Electron,记录下来. 说起WEB桌面程序,当前最火的就是Electron了. Electron的架构用一句话总结,就是一个main.js进程加上一个或数个chrom ...

  7. freeMarker(九)——程序开发指南补充知识

    学习笔记,选自freeMarker中文文档,译自 Email: ddekany at users.sourceforge.net 1.变量.范围 本章介绍当模板在访问变量时发生了什么事情,还有变量是如 ...

  8. freeMarker(七)——程序开发指南之数据模型

    学习笔记,选自freeMarker中文文档,译自 Email: ddekany at users.sourceforge.net 1.基本内容 在入门章节中, 我们已经知道如何使用基本的Java类(M ...

  9. 微信小程序 - 开发指南

    一.下载并安装开发工具 下载地址 二.创建项目 打开开发工具 添加项目 进入预览和调试界面 代码编辑器 编译并预览 三.启动流程 四.适用场景 五.技术框架 六.科普 [图片较大 - 点击查看]

随机推荐

  1. iOS-APNS证书申请与使用

    首先,需要一个pem的证书,该证书需要与开发时签名用的一致. 具体生成pem证书方法如下: 1. 登录到 iPhone Developer Connection Portal(http://devel ...

  2. Longest Substring Without Repeating Characters&lpar;C语言实现&rpar;

    Given a string, find the length of the longest substring without repeating characters. Examples: Giv ...

  3. 【BZOJ 4598】【SDOI 2016 Round2 Day1 T3】模式字符串

    2016-05-21因为BZOJ上“ 数据文件太过巨大,仅提供前三组数据测试.”所以我考场上写的60分的点分治交上去也A了. 我的这个点分治的时间复杂度是$O(Tnmlogn)$的,听题解时没听懂$O ...

  4. hdu 1026 Ignatius and the Princess I

    题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=1026 Ignatius and the Princess I Description The Prin ...

  5. win10更新时遇到错误0x80070002的正确处理方法

    win10更新Flash Player.或者在  “启用或关闭windows功能” 经常出现提示错误0x80070002,这要怎么解决呢?这里介绍下正确的错误代码0x80070002解决办法. 严肃提 ...

  6. Gulp自动构建Web前端程序

    这两天在一个朋友在项目上碰到了一个这样的问题,在运营过程中,用户在浏览器上对某个表单进行数据提交时,需要引入新的平台接口数据的业务,通过评估,开发团队马上修改了相关后台代码和部分的前端脚本代码,通过简 ...

  7. Java中synchronized和Lock的区别

    synchronized和Lock的区别synchronize锁对象可以是任意对象,由于监视器方法必须要拥有锁对象那么任意对象都可以调用的方法所以将其抽取到Object类中去定义监视器方法这样锁对象和 ...

  8. IOC杂谈(一)初识IOC

    初衷 发现学习东西不单只是看,用,还有很重要一点就是记录,不然过个几个月再用到相同的知识时,你会发现你已经丢得差不多了,故此开始在博客园记录的同时也与各位同行分享知识. 正题 关于IOC,在刚工作时就 ...

  9. 二层协议--STP协议总结

    生成树协议的技术实现与配置注意点 一.stp协议的用途 二.stp协议的运行机制 三.stp协议规范

  10. MyBatis学习——分步查询与延迟加载

    声明:面试是遇到延迟加载问题,在网页搜索到此篇文章,感觉很有帮助,留此学习之用! 一.分步查询 分步查询通常应用于关联表查询,如:电商平台,查询订单信息时需要查询部分的用户信息:OA系统查询个人信息时 ...