• Esper学习之十一:EPL语法(七)


     上一篇说到了EPL如何访问关系型数据库这种数据源,实际上别的数据源,比如:webservice、分布式缓存、非关系型数据库等等,Esper提供了统一的数据访问接口。然后今天会讲解如何创建另外一种事件类型——Schema

    1.Joining Method Invocation Results
    和执行sql的语法类似,调用方法的一种触发方式也是通过join别的事件的属性来达到效果,且调用方法的句子为from子句。语法如下:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. method:class_name.method_name(parameter_expressions)  

    method是固定关键字,class_name为类全名,方法名为返回外部数据的方法名,parameter_expressions为方法的参数列表,对应join的事件属性,多个属性之间用逗号分隔,参数整体用圆括号括起来。例如:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. select * from AssetMoveEvent, method:MyLookupLib.lookupAsset(assetId) // assetId为AssetMoveEvent的属性之一  

    除了简单join,还可以为join加上where条件过滤一些返回结果。例如:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. select assetId, assetDesc from AssetMoveEvent as asset,  
    2.     method:MyLookupLib.getAssetDescriptions() as desc    //调用的方法无参  
    3.         where asset.assetid = desc.assetid  

    Esper不仅能缓存执行sql的查询结果,也能缓存执行方法的查询结果,并且缓存策略也是两种:LRU和Expire Time。具体可以参考上一篇缓存配置章节。若存在返回结果,且缓存生效后,Esper会自动为返回结果简历索引,加快查询速度。配置范例如下:

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. // LRU Cache  
    2. <method-reference class-name="MyFromClauseWebServiceLib">  
    3.     <lru-cache size="1000"/>  
    4. </method-reference>  
    5.   
    6. // Expire Time Cache  
    7. <method-reference class-name="com.mycompany.MyFromClauseLookupLib">  
    8.     <expiry-time-cache max-age-seconds="10" purge-interval-seconds="10" ref-type="weak"/>  
    9. </method-reference>  

    class-name表示方法所在的类,可以是类全名,可以只有类名,前提是包已经import。其他参数的解释请参见上一篇缓存配置章节

    2.Polling Method Invocation Results via Iterator
    除了Join别的事件来触发查询方法,进而触发Listener,Esper还支持通过API直接执行方法。例如:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. select * from method:MyLookupLib.getAssetDescriptions(category_var)   // category_var为注册到引擎的变量。  


    3.Method Definition
    为了能够以一种统一的结构访问外部数据(RDBMS除外),Esper提供了调用静态方法的形式访问外部数据。具体解释如下:
    a.返回数据的方法必须是公共静态方法。方法参数可以有多个也可以没有。
    b.如果返回一条数据或无返回数据,则方法的返回类型可以是Java类或者Map类型数据。如果返回多条数据(包括一条),则方法返回类型必须是Java类的数组或者Map数组。
    c.如果方法的返回类型是Java类或者Java类数组,则Java的类定义必须包含针对属性的get方法。
    d.如果方法的返回类型是Map或者Map数组,则Map的key-value定义固定为<String, Object>。
    e.如果返回的数据是Map或者Map数组,除了定义返回数据的方法外,还要定义返回元数据的方法(这个元数据针对返回的数据)。方法是公共静态方法,且必须是无参数方法。方法返回类型为Map<String, Class>,String表示返回的数据的名称,Class表示返回的数据的类型。返回元数据的方法名称=返回数据的方法名称+Metadata。

    下面举一个完整的例子总结前面说的三点:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package example;  
    2.   
    3. /** 
    4.  * 返回Java类型的外部数据 
    5.  * 
    6.  * Created by Luonanqin on 2/16/14. 
    7.  */  
    8.   
    9. class JavaObject {  
    10.     private String name;  
    11.     private int size;  
    12.   
    13.     public String getName() {  
    14.         return name;  
    15.     }  
    16.   
    17.     public void setName(String name) {  
    18.         this.name = name;  
    19.     }  
    20.   
    21.     public int getSize() {  
    22.         return size;  
    23.     }  
    24.   
    25.     public void setSize(int size) {  
    26.         this.size = size;  
    27.     }  
    28.   
    29.     public String toString() {  
    30.         return "JavaObject{" + "name='" + name + ''' + ", size=" + size + '}';  
    31.     }  
    32. }  
    33.   
    34. public class InvocationMethodJava {  
    35.   
    36.     public static JavaObject[] getJavaObject(int times) {  
    37.         JavaObject[] javaObjects = new JavaObject[2];  
    38.         JavaObject javaObject1 = new JavaObject();  
    39.         javaObject1.setName("javaObject1");  
    40.         javaObject1.setSize(1 * times);  
    41.         JavaObject javaObject2 = new JavaObject();  
    42.         javaObject2.setName("javaObject2");  
    43.         javaObject2.setSize(2 * times);  
    44.   
    45.         javaObjects[0] = javaObject1;  
    46.         javaObjects[1] = javaObject2;  
    47.   
    48.         return javaObjects;  
    49.     }  
    50. }  
    51.   
    52. package example;  
    53.   
    54. import java.util.HashMap;  
    55. import java.util.Map;  
    56.   
    57. /** 
    58.  * 返回Map类型的外部数据 
    59.  * 
    60.  * Created by Luonanqin on 2/16/14. 
    61.  */  
    62. public class InvocationMethodMap {  
    63.   
    64.     public static Map<String, Object> getMapObject() {  
    65.         Map<String, Object> map = new HashMap<String, Object>();  
    66.         map.put("name", "mapObject1");  
    67.         map.put("size", 1);  
    68.   
    69.         return map;  
    70.     }  
    71.   
    72.     public static Map<String, Class> getMapObjectMetadata() {  
    73.         Map<String, Class> map = new HashMap<String, Class>();  
    74.         map.put("name", String.class);  
    75.         map.put("size", int.class);  
    76.   
    77.         return map;  
    78.     }  
    79. }  
    80.   
    81. package example;  
    82.   
    83. import java.util.Iterator;  
    84.   
    85. import com.espertech.esper.client.EPAdministrator;  
    86. import com.espertech.esper.client.EPRuntime;  
    87. import com.espertech.esper.client.EPServiceProvider;  
    88. import com.espertech.esper.client.EPServiceProviderManager;  
    89. import com.espertech.esper.client.EPStatement;  
    90. import com.espertech.esper.client.EventBean;  
    91. import com.espertech.esper.client.UpdateListener;  
    92.   
    93. /** 
    94.  * Created by Luonanqin on 2/16/14. 
    95.  */  
    96.   
    97. class Times {  
    98.     private int times;  
    99.   
    100.     public int getTimes() {  
    101.         return times;  
    102.     }  
    103.   
    104.     public void setTimes(int times) {  
    105.         this.times = times;  
    106.     }  
    107. }  
    108.   
    109. class InvocationMethodListener implements UpdateListener {  
    110.   
    111.     public void update(EventBean[] newEvents, EventBean[] oldEvents) {  
    112.         if (newEvents != null) {  
    113.             System.out.println(newEvents[0].getUnderlying());  
    114.             System.out.println(newEvents[1].getUnderlying());  
    115.         }  
    116.     }  
    117. }  
    118.   
    119. public class InvocationMethodTest {  
    120.     public static void main(String arg[]) {  
    121.         EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();  
    122.         EPRuntime runtime = epService.getEPRuntime();  
    123.         EPAdministrator admin = epService.getEPAdministrator();  
    124.   
    125.         /** 
    126.          * 调用外部方法返回Java类型数据 
    127.          */  
    128.         String timesName = Times.class.getName();  
    129.         String ijName = InvocationMethodJava.class.getName();  
    130.         String epl1 = "select ij.* from " + timesName + " as t, method:" + ijName + ".getJavaObject(times) as ij";  
    131.         System.out.println(epl1+" ");  
    132.   
    133.         EPStatement state1 = admin.createEPL(epl1);  
    134.         state1.addListener(new InvocationMethodListener());  
    135.   
    136.         Times times = new Times();  
    137.         times.setTimes(2);  
    138.   
    139.         runtime.sendEvent(times);  
    140.   
    141.         System.out.println("");  
    142.   
    143.         /** 
    144.          * 调用外部方法返回Map类型数据 
    145.          */  
    146.         String imName = InvocationMethodMap.class.getName();  
    147.         String epl2 = "select * from method:" + imName + ".getMapObject()";  
    148.         System.out.println(epl2+" ");  
    149.   
    150.         EPStatement state2 = admin.createEPL(epl2);  
    151.         Iterator<EventBean> iter = state2.iterator();  
    152.         while (iter.hasNext()) {  
    153.             EventBean event = iter.next();  
    154.             System.out.println(event.getUnderlying());  
    155.         }  
    156.     }  
    157. }  

    4.Declare an Event Type by Providing Names and Types
           我曾经在《Esper学习之二:事件类型》里说过,事件类型的定义可以是POJO,数组,Map,或者XML。实际上还有另一种定义事件类型的方法,那就是schema。这个schema可不是数据库中的schema,而是用EPL定义的事件类型,所以说此类事件类型是针对Esper设计的,并不能拿出来通用。

    我们先从语法开始说起。

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. create [map | objectarray] schema schema_name [as]  
    2.  (property_name property_type [,property_name property_type [,...])  
    3.  [inherits inherited_event_type[, inherited_event_type] [,...]] [starttimestamp timestamp_property_name]  
    4.  [endtimestamp timestamp_property_name]  
    5.  [copyfrom copy_type_name [, copy_type_name] [,...]]  

    解释如下:

    a.map objectarray分别表示当前定义的schema是map结构还是数组结构。
    b.schema_name表示schema的名字,全局唯一。as为可选内容。
    c.property_name表示schema所包含的属性名称,property_type为属性的类型,多个属性用逗号分隔,所有属性用圆括号括起来可以使用的类型有:int、String等已经内置的Java类型;已经通过Configuration接口注册的事件类型,比如Map,数组,schema等;可以使用POJO类,如果没有import就要写全名,否则写类名即可。
    d.inherits表示继承别的事件类型,后面跟着的是要继承的事件类型。如果使用了继承,则当前定义的schema包含了继承的事件类型的所有属性。并且可以继承多个,用逗号分隔。
    e.starttimestamp和endtimestamp是两个特殊的关键字。单独使用starttimestamp时,表示为schema记一个时间戳。后面跟着已经声明的属性并且能够返回一个data-time值。如果联合endtimestamp使用,则表示这个schema只能在某段事件内使用。后面跟着的同样也是已经声明的属性并且能够返回一个data-time值。注意endtimestamp不能单独使用。
    f.copyfrom表示复制别的事件的所有属性到当前定义的schema中,并且可以copy多个事件,用逗号分隔。


    下面用几个例子来一一展示如何使用上面的语法:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. // 声明SecurityEvent  
    2. create schema SecurityEvent as (ipAddress string, userId String, numAttempts int)  
    3.   
    4. // 声明AuthorizationEvent,并且包含com.mycompany.HostNameInfo类型的hostinfo属性  
    5. create schema AuthorizationEvent(group String, roles String[], hostinfo com.mycompany.HostNameInfo)  
    6.   
    7. // 声明CompositeEvent,并且包含了SecurityEvent数组作为innerEvents属性的类型  
    8. create schema CompositeEvent(group String, innerEvents SecurityEvent[])  
    9.   
    10. // 声明WebPageVisitEvent,自己定义了userId属性,并且继承了PageHitEvent的所有属性  
    11. create schema WebPageVisitEvent(userId String) inherits PageHitEvent  
    12.   
    13. // 声明RoboticArmMovement,并且开始于startts,结束于endts  
    14. create schema RoboticArmMovement (robotId string, startts long, endts long) starttimestamp startts endtimestamp endts  
    15.   
    16. // 声明ExtendedSecurityEvent,并且复制SecurityEvent事件的所有属性到ExtendedSecurityEvent  
    17. create schema ExtendedSecurityEvent (userName string) copyfrom SecurityEvent  
    18.   
    19. // 声明WebSecurityEvent,并且复制SecurityEvent和WebPageVisitEvent事件的所有属性到WebSecurityEvent  
    20. create schema WebSecurityEvent (userName string) copyfrom SecurityEvent, WebPageVisitEvent  

    这里要额外说一下,继承不仅仅继承了事件的属性,如果被继承的事件定义了starttimestamp或者endtimestamp,同样也会被继承下来。但是copyfrom是不会吧starttimestamp和endtimestamp复制的。这点一定要注意。

    对于map和objectarray这两个关键字,可以用注解达到同样的效果。例如:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. // 声明数组类型的schema  
    2. create objectarray schema SchemaTest1 as (prop1 string);  
    3. ... equals ...  
    4. @EventRepresentation(array=true)create schema SchemaTest1 as (prop1 string);  
    5.   
    6. // 声明Map类型的schema  
    7. create map schema SchemaTest2 as (prop1 string);  
    8. ... equals ...  
    9. @EventRepresentation(array=false)create schema SchemaTest2 as (prop1 string);  

    5.Declare Variant Stream
    Variant Stream简单来说就是包含了各种不同的事件类型的事件类型。所以语法也很明了:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. create variant schema schema_name [as] eventtype_name|* [, eventtype_name|*] [,...]  

    variant为关键字,表明这是Variant Stream,eventtype_name为事件的定义名,多个事件定义用逗号分隔开。*表示接收任何的事件类型,不过一般来说没有需求会到这种程度。举例如下:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. // 声明SecurityVariant,包含了LoginEvent和LogoutEvent  
    2. create variant schema SecurityVariant as LoginEvent, LogoutEvent  
    3. // 声明AnyEvent,包含任何类型的事件  
    4. create variant schema AnyEvent as *  

    以上就是调用外部方法以及schema的创建讲解,尤其是schema的创建,可能大家会用的更多一些,最好能记住。

  • 相关阅读:
    适配问题
    屏幕适配
    软键盘适配
    即时通讯
    缩减APK包大小
    基于RulesEngine的业务规则实现
    基于NXBRE规则引擎实现的柔性折扣策略
    SQL Server代码如何快速格式化
    SQL Server 跨库同步数据
    HTML+AngularJS+Groovy如何实现登录功能
  • 原文地址:https://www.cnblogs.com/yudar/p/4872651.html
Copyright © 2020-2023  润新知