天天看點

Ignite SQL網格快速學習(二)1.設定schema模式和索引index(靜态版)2. 分布式DML3. 子查詢4. 分布式DDL5. 查詢逾時6. 自定義SQL函數

在快學(一),我們講了比較常用的一些SQL查詢方式,本章的目标:
1.鞏固基礎知識
2.延展特性
我建議每一位在看的朋友,一定要把快學(一)看一下,當然可以先看完快學(二)再回頭看快學(一)也是不耽誤的。
           

1.設定schema模式和索引index(靜态版)

1.1 樣例準備

public class Person {
    private static final AtomicLong ID_GEN = new AtomicLong();
    @QuerySqlField(index = true)
    public Long id;
    @QuerySqlField(index = true)
    public Long companyId;
    @QuerySqlField
    public String firstName;
    @QuerySqlField
    public String lastName;
    @QuerySqlField
    public String resume;
    @QuerySqlField(index = true)
    public double salary;
    private transient AffinityKey<Long> key;
    public Person() {
    }
}
           
CacheConfiguration<Long, Person> personConfig = new CacheConfiguration<Long, Person>("CACHE_ONLY_PERSON");
            personConfig.setIndexedTypes(Long.class,Person.class);
            try (IgniteCache<Long, Person> cacheOnlyPerson = ignite.getOrCreateCache(personConfig)) {
    ...
}
           

1.2 概述

在上述的Person類中,我們通過Ignite的注解,實作了類到表(緩存中的)的一種關聯映射,并且通過@QuerySqlField(index = true)設定了索引,這裡需要注意的是@QuerySqlField這樣的注解僅僅可以使得該屬性是Ignite可見的,不帶蓋注解的屬性,Ignite是不可見的。如果想設定為索引項,那麼就要像示例中,加上index=true屬性。
我們這裡隻是做了索引與可見屬性的辨別,下面還需要進行索引注冊。
Ignite的索引注冊比較特别,我們來看第二段代碼。我們調用了CacheConfiguration#setIndexedTypes(...).該方法的入參是一個可變類型,我們可以傳遞多個class對象進去。但是官文明确要求,這個參數的數量必須是2的倍數,因為奇數作為鍵的class對象類型,而偶數則是相應的值的class對象類型。比如說我們的測試用例中,我們在該緩存中緩存的隻能是Long類型的鍵和Person類型的值,那麼我們就按照執行個體這樣填寫。通過這種注冊,Ignite即知道了要為Person設定索引。
在這裡引申一個組索引的概念:當查詢條件複雜時可以使用多字段索引來加快查詢的速度,這時可以用@QuerySqlField.Group注解。如果希望一個字段參與多個分組索引時也可以将多個@QuerySqlField.Group注解加入orderedGroups/groups中。 
我下面分别進行示範
           
public class Person {
    @QuerySqlField(index = true)
    public Long id;
    @QuerySqlField(index = true)
    public Long companyId;
    @QuerySqlField(groups= {"A"})
    public String firstName;
    @QuerySqlField(groups= {"A"})
    public String lastName;
    @QuerySqlField(orderedGroups={@QuerySqlField.Group(name = "B", order = )})
    public String resume;
    @QuerySqlField(orderedGroups={@QuerySqlField.Group(name = "B", order = , descending = true)})
    public double salary;
}
           
上述用例展示的就是分組索引,我們一共做了兩種分組方式,一種是以group的形式,另外就是以orderFroups形式。差別還是很大的,我從頭開始講:

@QuerySqlField是以個注解,維護着String[]形式的groups屬性個其内部類Group[]的orderGroups,這兩個都是進行分組索引的,我先分别講解功能,然後再剖析其差別:
1.groups:這種形式的分組,可以将多個字段,聯合成一個組索引,我們在上述代碼中是以A形式命名的組索引,這樣,lastname和firstname都會被聯合成一個組索引,類似于(firstname asc,lastname,asc).我們設想一個這樣的情景:select * from Person where Person.firstname = ? and Person.lastname = ?。如果按照我們之前的方式,即單個屬性維持自己的索引,也就是@QuerySqlField(index=true)的話,我們為屬性firstname和lastname都加上這個注解,效率還是沒有我們現在的這種分組形式快!!!為什麼呢??因為在Ignite中,SQL引擎隻能在查詢中每表出現一個索引。是以,即使你在每個屬性上都加了索引,SQL引擎隻會選取一個索引來加速我們的SQL操作。是以組索引就派上用場了。在複雜查詢時候,組索引可以顯著改善我們的查詢速度。
groups屬性後跟的是一個數組,因為該屬性是String[],是以隻能寫一個name值,同一組的,名字要相同,你如果要為一個屬性設定多個多個組,那就寫多個值
2.orderGroups:這種形式的分組,與上述基本類似,唯一差別則是,orderGroup做為注解@QuerySqlField的一個屬性,其類型簽名是其内部類Group形式的,它是由自己的屬性的。我們來看我們的樣例中的注釋,即可弄明白。我着重講一下其屬性,
①name:同上,就是分組的名字,相同組内的要用一緻的名字。
②descending:是否降序排序,預設是false,即asc,設定為true的話就是desc了,那麼我們可以預見到我們的索引B是長的這個樣子(salary desc,resume asc).這樣的好處是但我們在SQL:select * from Person order by salary desc,resume asc中需要做排序操作時候,而且還是逆序排序,那麼這種方式可以加快速度,因為我們的索引已經排好序了,但是如果使用上述的groups形式,它隻能産生類似于(salary asc,resume asc)的索引,效率還是低于我們以orderGroups形式産生的索引的。
③order:這個值必須設定,定義組中該字段的排序順序。參考在descending講的,為什麼salary會在前面,以及我寫的SQL中排序的先後順序。

PS:注意啊各位:
在@QuerySqlField(orderedGroups={...})之外使用@QuerySqlField.Group注釋字段,是無效的。是以,按照我寫的這樣繼續拓展即可。
           

1.3 忠告

當為您的ignite應用程式選擇索引時,您應該考慮多種因素。
1.索引不是免費的。它們消耗記憶體,而且每個索引都需要分别更新,是以當設定更多索引時,緩存更新性能可能會更差。
最重要的是,優化器可能會選擇錯誤的索引來運作查詢,進而犯更多錯誤。
2.索引隻是排序的資料結構。如果你為字段(a、b、c)定義一個索引,那麼記錄将首先被a排序,然後是b,然後是c。
PS:排序索引的例子
        | A | B | C |
        | 1 | 2 | 3 |
        | 1 | 4 | 2 |
        | 1 | 4 | 4 |
        | 2 | 3 | 5 |
        | 2 | 4 | 4 |
        | 2 | 4 | 5 |
任意條件,比如a = 1 and b > 3,都會被視為有界範圍,在log(N)時間内兩個邊界在索引中可以被快速檢索到,
然後結果就是兩者之間的任何資料。 
下面的條件會使用索引: 
        a = ? 
        a = ? and b = ? 
        a = ? and b = ? and c = ? 
從索引的角度,條件a = ?和c = ?不會好于a = ? 
明顯地,半界範圍a > ?可以工作得很好。
3.單個字段上的索引在以相同字段開始的多個字段上的組索引不比組索引好(a)的索引與(a、b、c)相同。是以,最好使用組索引。
           

2. 分布式DML

what's DML??
MERGE UPDATE DELETE INSERT
           

2.1 準備工作

public class Person {
    private static final AtomicLong ID_GEN = new AtomicLong();
    @QuerySqlField(index = true)
    public Long id;
    @QuerySqlField(index = true)
    public Long companyId;
    @QuerySqlField(groups= {"A","single"})
    public String firstName;
    @QuerySqlField(groups= {"A"})
    public String lastName;
    @QuerySqlField(orderedGroups={@QuerySqlField.Group(name = "B", order = , descending = true)})
    public String resume;
    @QuerySqlField(orderedGroups={@QuerySqlField.Group(name = "B", order = , descending = true)})
    public double salary;
    private transient AffinityKey<Long> key;
}
           
這是我們将會使用的POJO,一定注意它的合格注解,我們一共注解了6個屬性,而最後的并置鍵沒有注解,因為我們測試場景不使用它。
           

2.2 分布式DML

public static void main(String[] args) {
        try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) {
            CacheConfiguration<Long, Company> companyConfig = new CacheConfiguration<Long, Company>(
                    "CACHE_ONLY_COMPANY");
            companyConfig.setIndexedTypes(Long.class, Company.class);
            CacheConfiguration<Long, Person> mergeConfig = new CacheConfiguration<Long, Person>(
                    "CACHE_MERGE_PERSON");
            mergeConfig.setIndexedTypes(Long.class, Person.class);
            CacheConfiguration<Long, Person> personConfig = new CacheConfiguration<Long, Person>("CACHE_ONLY_PERSON");
            personConfig.setIndexedTypes(Long.class,Person.class);
            try (IgniteCache<Long, Company> cacheOnlyCompany = ignite.getOrCreateCache(companyConfig);
                    IgniteCache<Long, Person> cacheOnlyPerson = ignite.getOrCreateCache(personConfig);
                    IgniteCache<Long, Person> cacheMergePerson = ignite.getOrCreateCache(mergeConfig)) {
                cacheOnlyCompany.put(L, new Company("ultrapower"));
                // DML INSERT(1)
                cacheOnlyPerson.query(new SqlFieldsQuery("INSERT INTO Person(_key,_val) VALUES(?,?)").setArgs(L,new Person(L,L,"piemon","jax",D,"Good")));
                // DML INSERT(2)
                cacheOnlyPerson.query(new SqlFieldsQuery("INSERT INTO Person(_key,id,companyId,firstName,lastname,salary,resume) VALUES(?,?,?,?,?,?,?)").setArgs(L,L,L,"piemon","jax",D,"Bad"));
                //DML SELECT
                QueryCursor<Entry<Long, Person>> person = cacheOnlyPerson.query(new SqlQuery<Long, Person>(Person.class,"SELECT * FROM Person where Person.resume = ?").setArgs("Bad"));
                System.out.println(person.getAll().get());
                System.out.println("update ::::");
                //DML UPDATE
                FieldsQueryCursor<List<?>> update = cacheOnlyPerson.query(new SqlFieldsQuery("update Person set firstname = ? where _key= ?").setArgs("anokata",L));
                System.out.println("update影響行數:" + update.getAll().get());
                System.out.println("delete :::");
                //DML DELETE
                FieldsQueryCursor<List<?>> delete = cacheOnlyPerson.query(new SqlFieldsQuery("DELETE FROM  Person  where _key= ?").setArgs(L));
                System.out.println("update影響行數:" + delete.getAll().get());
                //DML MERGE
                System.out.println("merge :::");
                cacheMergePerson.query(new SqlFieldsQuery("MERGE INTO CACHE_MERGE_PERSON.Person(_key,id,companyId,firstName,lastname,salary,resume)(SELECT _key+1000,id+1000,companyId,firstName,lastname,salary,resume FROM CACHE_ONLY_PERSON.Person WHERE CACHE_ONLY_PERSON.Person._key = ?)").setArgs(L));
                QueryCursor<Entry<Long, Person>> merge = cacheMergePerson.query(new SqlQuery<Long,Person>(Person.class, "from Person"));
                merge.getAll().stream().forEach(System.out::println);
            }
        }
    }
           

日志輸出

[15:11:48] Topology snapshot [ver=1, servers=1, clients=0, CPUs=4, heap=0.87GB]
insert :::
Entry [key=11, val=Person [id=11, companyId=1, lastName=jax, firstName=piemon, salary=20000.0, resume=Bad]]
update ::::
update影響行數:[1]
delete :::
update影響行數:[1]
merge :::
Entry [key=1011, val=Person [id=1011, companyId=1, lastName=jax, firstName=piemon, salary=20000.0, resume=Bad]]
[15:11:48] Ignite node stopped OK [uptime=00:00:00:909]
           
我們的日志列印的是我們查詢出的Person。但是一定要注意,我們查詢的是Person存儲時候的方式
           

2.3 總結

對于DML的Query操作,傳回的結果是本次操作所影響的行數!!!上述的日志有展現~~
           

2.3.1 INSERT

MERGE和INSERT指令的不同在于,後者添加的條目必須是緩存中不存在的。 
如果要把一個鍵值對插入緩存,那麼最後,INSERT語句會被轉換為cache.putIfAbsent(...)操作,否則,如果插入的是多個鍵值對,那麼DML引擎會為每個對建立一個EntryProcessor,然後使用cache.invokeAll(...)将資料注入緩存。 

我們在執行個體代碼中,以三種方式存儲了資料,分别是API:IgniteCache#put和兩種SQL方式。我們接下來分别講解
1.API:這是每種記憶體資料庫都有的操作,不過多将了
2.(_key,_val):我們之前介紹過。Ignite有兩個關鍵字,即:_key,_val,分别辨別着鍵和值,我們這裡也是如這般用法,我們将_key指派1L,将_val指派一個Person對象
3.(_key,id,compantId.....):這應該算是我們比較常用的SQL寫法了,但是這裡必須注意的一點就是,你在_key後面所加的屬性名,必須在POJO中以@QuerySqlField注釋,否則Ignite無法将查詢的資料封裝為你想要的資料類型。

Ignite的INSERT内是可以寫參數清單以及子查詢的,我舉個例子:
           
子查詢的例子,我再merge的測試用例中實作的,可以參考哦~~

ignite将所有資料以鍵-值對的形式存儲在記憶體中,是以所有DML相關操作都被轉換成相應的基于像cache.put(...)或者cache.invokeAll(...)這樣的指令指令。
           

2.3.2 UPDATE

開始時,SQL引擎會根據UPDATE語句的WHERE條件生成并且執行一個SELECT查詢,然後會修改滿足條件的已有值。 修改的執行是利用cache.invokeAll(...)實作的。基本上來說,這意味着一旦SELECT查詢的結果準備好,SQL引擎就會準備一定數量的EntryProcessors然後執行cache.invokeAll(...)操作,下一步,EntryProcessors修改完資料之後,會進行額外的檢查來確定在SELECT和資料實際更新之間沒有其他幹擾。
了解該功能并不難,這裡需要注意的是:
    UPDATE不可以修改_key
    原因是緩存鍵的狀态決定了内部資料的布局及其一緻性(鍵的哈希及其關系,索引完整性),是以目前除非先将其删除,否則無法更新緩存鍵。比如下面的查詢: 
    UPDATE _key = 11 where _key = 10; 
    會導緻下面的緩存操作: 
    val = get(10); 
    put(11, val); 
    remove(10);
           

2.3.3 DELETE

DELETE語句的執行分為兩個階段,類似于UPDATE語句的執行。
首先,使用SELECT查詢,SQL引擎收集那些滿足DELETE語句中WHERE子句的鍵。接下來,把所有的緩存鍵都放好後,它建立了一些EntryProcessors并以cache.invokeAll(…)來執行它們。當資料被删除時,會執行額外的檢查以確定沒有人幹擾到資料的SELECT和實際删除。
           

2.3.4 MERGE

MERGE是一個非常簡單的操作,因為它會被翻譯成cache.put(...)或者cache.putAll(...),具體是哪一個,取決于MERGE語句涉及的要插入或者要更新的記錄的數量。
我們執行個體中示範了MERGE是支援子查詢的,當然他也支援參數清單,與INSERT是基本類似的。
           

3. 子查詢

INSERT和MERGE語句中的子查詢和UPDATE和DELETE操作自動生成的SELECT查詢一樣,如有必要都會被分布化然後執行,要麼是并置,要麼是非并置的模式。 我們在之前已經解釋了Query的并置與非并置,忘記的傳回去複習。
我們這裡需要重點介紹的是,如果WHERE語句裡面有一個子查詢,那麼他是不會以非并置的分布式模式執行的,子查詢始終都會以并置的模式在本地節點上執行。 
下面我們選用之前的一個例子,具體代碼就不貼了,就是我在講分布式非并置關聯查詢時候使用的資料,下面我們直接進到用例的重要部分
           

3.1 Where中的子查詢

private static void distributedNonCollocatedJoin(IgniteCache<Long, Person> cacheOnlyPerson) {
        final String ORG_CACHE = "CACHE_ONLY_COMPANY";
        QueryCursor<Entry<AffinityKey<Long>, Person>> query = cacheOnlyPerson.query(new SqlQuery<AffinityKey<Long>, Person>(Person.class, "from Person").setLocal(true));
        print("Local all persons:", query);
        IgniteCache<Long, Company> cacheOnlyCompany = Ignition.ignite().cache("CACHE_ONLY_COMPANY");
        QueryCursor<Entry<Long, Company>> query2 = cacheOnlyCompany
                .query(new SqlQuery<Long, Company>(Company.class, "from Company").setLocal(true));
        print("Local all company:", query2);





        String sql = "delete from Person as p where p.companyId in (select _key from CACHE_ONLY_COMPANY.Company)";
        SqlFieldsQuery sqlFieldsQuery = new SqlFieldsQuery(sql);
        sqlFieldsQuery.setDistributedJoins(true);
        cacheOnlyPerson.query(sqlFieldsQuery);


        System.out.println("------------------------------------------");
        QueryCursor<Entry<Long,Person>> result = cacheOnlyPerson.query(new SqlQuery<Long,Person>(Person.class, "from Person"));
        result.getAll().stream().forEach(System.out::println);

    }
           

輸出日志

[16:23:56] Topology snapshot [ver=2, servers=2, clients=0, CPUs=4, heap=1.7GB]

>>> Local all persons:
>>>     Entry [key=3, val=Person [id=3, companyId=11, lastName=Smith, firstName=John, salary=3000.0, resume=John Smith has Bachelor Degree.]]

>>> Local all company:
>>>     Entry [key=11, val=Company [id=11, name=ultrapower93]
------------------------------------------
Entry [key=4, val=Person [id=4, companyId=11, lastName=Smith, firstName=Jane, salary=4000.0, resume=Jane Smith has Master Degree.]]
[16:23:58] Ignite node stopped OK [uptime=00:00:02:472]
           

3.3總結

我們上面隻貼出了SQL操作的部分,其他因為以前用的太多了,就不貼出來占地方了。
首先,我一共啟動的了兩個伺服器節點。
通過上述的日志,我們也可以看到本地節點上資料并不多,隻有一個Company和Person,其他的資料自然是在另外一個節點上。我下面草拟一下資料的排布
A節點:
    Person[id=1,companyId=1]
    Person[id=2,companyId=1]
    Person[id=4,companyId=11]
    Company[id=1]
B節點(即我們上述方法執行所在的節點,我們有用Cleint)
    Person[id=3,CompanyId=11]
    Company[id=11]
然後我們來看最終結果。剩下的是Person[id=4,companyId=11]
其實到這裡已經明了了,where字句中的子查詢确實隻是在本地做關聯查詢,并沒有分布式!!
是以,如果必須在where中使用子查詢的話,一定要確定子查詢資料采取了必要的并置,杜絕我們示例這種情況發生。
           

4. 分布式DDL

what's DDL
資料定義語言(DDL)
Apache ignite支援使用資料定義語言(DDL)語句在運作時建立和删除SQL索引。原生的Ignite SQL API以及JDBC和ODBC驅動都可以用于SQL模式的修改。
           

4.1 建表

Apache ignite支援在運作時建立和删除SQL表和索引的資料定義語言(DDL)語句。我們之前講過的Query都可以激活語句,也可以使用JDBC和ODBC驅動程式來修改SQL模式。
文法:
CREATE TABLE [IF NOT EXISTS] tableName (tableColumn [, tableColumn]...[, PRIMARY KEY (columnName [, columnName]...)] )[WITH "paramName=paramValue [,paramName=paramValue]..."]


tableColumn := columnName columnType [PRIMARY KEY]
           

4.2 建索引

4.2.1 文法

建立一般順序的索引文法:
CREATE [SPATIAL] INDEX [IF NOT EXISTS] indexName ON tableName (indexColumn, ...)
建立符合索引的文法:
CREATE INDEX idx_person_name_birth_date ON Person (name ASC, birth_date DESC)
建立地理空間索引的文法:
CREATE SPATIAL INDEX idx_person_address ON Person (address)
删除索引
DROP INDEX [IF EXISTS] indexName
           

4.2.2 建立索引

public class Company {

    private static final AtomicLong ID_GEN = new AtomicLong();

    @QuerySqlField(index = true)
    private Long id;

    @QuerySqlField()
    private String name;
}
           
public static void main(String[] args) {
        try(Ignite ignite = Ignition.start("examples/config/example-ignite.xml")){
            CacheConfiguration<Long, Company> companyConfig = new CacheConfiguration<Long, Company>(
                    "CACHE_ONLY_Company");
            companyConfig.setIndexedTypes(Long.class, Company.class);

            try (IgniteCache<Long, Company> cacheOnlyCompany = ignite.getOrCreateCache(companyConfig);) {
                String sql = "CREATE INDEX idx_company_name on Company(name)";

                FieldsQueryCursor<List<?>> query = cacheOnlyCompany.query(new SqlFieldsQuery(sql));
                query.getAll().stream().forEach(System.out::println);
                String sql2 = "CREATE INDEX idx_company_id_name on Company(id DESC,name ASC)";
                FieldsQueryCursor<List<?>> query2 = cacheOnlyCompany.query(new SqlFieldsQuery(sql2));
                query2.getAll().stream().forEach(System.out::println);
                String sql3 = "CREATE SPATIAL INDEX idx_company_name ON Company (name)";
                FieldsQueryCursor<List<?>> query3 = cacheOnlyCompany.query(new SqlFieldsQuery(sql3));
                query3.getAll().stream().forEach(System.out::println);
            }
        }
    }
           

輸出日志

4.2.3 總結

通過DDL文法,使得我們可以在運作時建立索引。删除索引太簡單,就不寫了。
執行個體應該是最好的語言,我相信絕大多數的看官都會。
這裡需要着重說的是,做地理空間索引時候,必須加入如下依賴
           
<repositories>
        <repository>
            <id>GridGain External Repository</id>
            <url>http://www.gridgainsystems.com/nexus/content/repositories/external</url>
        </repository>
    </repositories>
    ...
        <dependency>
            <groupId>org.apache.ignite</groupId>
          <artifactId>ignite-geospatial</artifactId>
          <version></version>
        </dependency>
           
作為拓展項目,後面還會說這個點,各位看官可以在後面學習。
           

4.3 拓展

Ignite的DDL支援jdbc拓展,我們看下面的例子:
           
Class.forName("org.apache.ignite.IgniteJdbcDriver");

Connection conn = DriverManager.getConnection(
    "jdbc:ignite:cfg://file:///etc/config/ignite-jdbc.xml");

try (Statement stmt = conn.createStatement()) {
    stmt.execute("CREATE INDEX idx_company_name ON Company (name)");
}
           
作為一個拓展點,當無法擷取Ignite執行個體時候,您可以通過這種方式來操作DDL
           

5. 查詢逾時

5.1 準備資料

還是老一樣的資料,就不貼了
           

5.2 逾時處理兩種方式

public static void main(String[] args) {
        try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) {
            CacheConfiguration<Long, Company> companyConfig = new CacheConfiguration<Long, Company>(
                    "CACHE_ONLY_COMPANY");
            companyConfig.setIndexedTypes(Long.class, Company.class);

            CacheConfiguration<Long, Person> personConfig = new CacheConfiguration<Long, Person>("CACHE_ONLY_PERSON");
            personConfig.setIndexedTypes(Long.class,Person.class);
            try (IgniteCache<Long, Company> cacheOnlyCompany = ignite.getOrCreateCache(companyConfig);
                    IgniteCache<Long, Person> cacheOnlyPerson = ignite
                            .getOrCreateCache(personConfig)) {
                // 初始化資料
                initData(cacheOnlyCompany, cacheOnlyPerson);
                timeout(cacheOnlyPerson);
                shutdown(cacheOnlyPerson);
            }
        }
    }
           
private static void timeout(IgniteCache<Long, Person> cacheOnlyPerson) {
        // TODO Auto-generated method stub
        QueryCursor<Entry<Long, Person>> person = cacheOnlyPerson.query(new SqlQuery<Long,Person>(Person.class,"from Person").setTimeout(, TimeUnit.SECONDS));
    }
           
private static void shutdown(IgniteCache<Long, Person> cacheOnlyPerson) {

        QueryCursor<Entry<Long, Person>> person = cacheOnlyPerson.query(new SqlQuery<Long,Person>(Person.class,"from Person"));
        person.close();
    }
           

5.2.1設定Query逾時時間(系統控制時間)

我們先來看設定timeout時間的方式,這種方式javaCompletableFuture的形式差不多,可以借鑒來了解。
SqlQuery和SqlFieldQuery均提供了這API,幫你斷掉失敗的查詢
           

5.2.2 close(使用者控制時間)

我們來看shutdown方法,代碼中我們并沒有對查詢Query設定逾時時間,也就是說如果資料可以的話,那麼就一直持續查詢。
我們可以自己維護一個定時器,當你感覺不像等待時候,可以調用查詢結果集的close方法,強制停掉查詢。
QueryCursor#close()方法的作用是:
關閉與此cursor(就是結果集)相關的所有資源。如果查詢已經在進行中(如果從另一個線程調用,這是可能的),将會嘗試去取消掉。對這種方法的連續調用沒有副作用。
生産中可以多采用timeout的的形式來提高系統的響應性~~
           

6. 自定義SQL函數

Ignite的SQL引擎支援通過額外用Java編寫的自定義SQL函數,來擴充ANSI-99規範定義的SQL函數集。 
一個自定義SQL函數僅僅是一個加注了@QuerySqlFunction注解的公共靜态方法。
           

6.1 準備函數

public class SqlFunctionEnjoy {

    @QuerySqlFunction
    public static double sqlFunction(double salary) {
        return salary + ;
    }
}
           
你需要做的就是這麼簡單,但是方法必須靜态,注解必須加!!!
           

6.2 SqlFunction

public static void main(String[] args) {
        try (Ignite ignite = Ignition.start("examples/config/example-ignite.xml")) {
            CacheConfiguration<Long, Company> companyConfig = new CacheConfiguration<Long, Company>(
                    "CACHE_ONLY_COMPANY");
            companyConfig.setIndexedTypes(Long.class, Company.class);

            CacheConfiguration<Long, Person> personConfig = new CacheConfiguration<Long, Person>("CACHE_ONLY_PERSON");
            personConfig.setIndexedTypes(Long.class,Person.class);
            personConfig.setSqlFunctionClasses(SqlFunctionEnjoy.class);
            try (IgniteCache<Long, Company> cacheOnlyCompany = ignite.getOrCreateCache(companyConfig);
                    IgniteCache<Long, Person> cacheOnlyPerson = ignite
                            .getOrCreateCache(personConfig)) {
                // 初始化資料
                initData(cacheOnlyCompany, cacheOnlyPerson);

                FieldsQueryCursor<List<?>> result = cacheOnlyPerson.query(new SqlFieldsQuery("select salary from Person as p where p.firstname=? and lastname = ?").setArgs("John","Doe"));
                Double salary = (Double) result.getAll().get().get();
                System.out.println("不調用函數的salary:" + salary);


                result = cacheOnlyPerson.query(new SqlFieldsQuery("select sqlFunction(salary) from Person as p where p.firstname=? and lastname = ?").setArgs("John","Doe"));
                salary = (Double) result.getAll().get().get();
                System.out.println("調用函數的salary:" +salary);
            }
        }
    }
           

輸出日志:

[18:47:43] Topology snapshot [ver=1, servers=1, clients=0, CPUs=4, heap=0.87GB]
不調用函數的salary:2000.0
調用函數的salary:3000.0
[18:47:43] Ignite node stopped OK [uptime=00:00:00:752]
           

6.3 總結

自定義函數的步驟:
1.在某一個類A中建立你的SQL函數,該函數必須靜态,必須辨別以@QuerySqlFunction注解
2.在業務用例中,通過CacheConfiguration#setSqlFunctionClasses(class...),将你函數所在的類的class執行個體注冊進去。Ignite會去自己找
3.在sql中可以像樣例中這樣使用自定義的sql函數啦。
該功能比較簡單,但是用處很大,切記。