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


      在esper的文档中,epl访问数据库的配置放在了比较靠后的位置,不过为了方便各位学习,这里会先说明和数据库交互的相关配置,然后再说epl怎么访问数据库。

    配置文件在官方esper包的etc文件夹下,大家可以参考着学习。

          

    1.连接数据库

    a.JNDI获取连接

    配置如下:

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <database-reference name="mydb1">  
    2.   <datasource-connection context-lookup-name="java:comp/env/jdbc/mydb">  
    3.     <env-property name="java.naming.factory.initial" value ="com.myclass.CtxFactory"/>  
    4.     <env-property name="java.naming.provider.url" value ="iiop://localhost:1050"/ >  
    5.   </datasource-connection>  
    6. </database-reference>  

    database-reference的name是要连接的数据库名字,其余的配置可参考JNDI的文档

    使用方法:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. if (envProperties.size() > 0) {  
    2.   initialContext = new InitialContext(envProperties);  
    3. else {  
    4.   initialContext = new InitialContext();  
    5. }  
    6. DataSource dataSource = (DataSource) initialContext.lookup(lookupName);  
    7. Connection connection = dataSource.getConnection();  

    更多内容可参考JNDI的文档

    b.从连接池获取连接

    配置如下:(以dbcp为例)

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <database-reference name="mydb3">  
    2.   <!-- For a complete list of properties see Apache DBCP. -->  
    3.   <datasourcefactory-connection class-name="org.apache.commons.dbcp.BasicDataSourceFactory">  
    4.     <env-property name="username" value ="myusername"/>  
    5.     <env-property name="password" value ="mypassword"/>  
    6.     <env-property name="driverClassName" value ="com.mysql.jdbc.Driver"/>  
    7.     <env-property name="url" value ="jdbc:mysql://localhost/test"/>  
    8.     <env-property name="initialSize" value ="2"/>  
    9.     <env-property name="validationQuery" value ="select 1 from dual"/>  
    10.   </datasourcefactory-connection>  
    11. <connection-lifecycle value="pooled"/>  
     
     
    
    
    
     
     
     
     

    </database-reference>

    
    

    相同的配置可以使用esper的api达到同样的效果。代码如下:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. Properties props = new Properties();  
    2. props.put("username", "myusername");  
    3. props.put("password", "mypassword");  
    4. props.put("driverClassName", "com.mysql.jdbc.Driver");  
    5. props.put("url", "jdbc:mysql://localhost/test");  
    6. props.put("initialSize", 2);  
    7. props.put("validationQuery", "select 1 from dual");  
    8.   
    9. ConfigurationDBRef configDB = new ConfigurationDBRef();  
    10. // BasicDataSourceFactory is an Apache DBCP import  
    11. configDB.setDataSourceFactory(props, BasicDataSourceFactory.class.getName());  
    12. configDB.setConnectionLifecycleEnum(ConfigurationDBRef.ConnectionLifecycleEnum.POOLED);  
    13. Configuration configuration = new Configuration();  
    14. configuration.addDatabaseReference("mydb3", configDB);  

    同样,也可以自己实现数据源。示例如下:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. configDB.setDataSourceFactory(props, MyOwnDataSourceFactory.class.getName());  
    2. ...  
    3. class MyOwnDataSourceFactory {  
    4.   public static DataSource createDataSource(Properties properties) {  
    5.     return new MyDataSourceImpl(properties);  
    6.   }   
    7. }  

    c.JDBC获取连接

    前提是要将对应的jdbc驱动假如classpath

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <database-reference name="mydb2">  
    2.   <drivermanager-connection class-name="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/mydb2" user="myuser" password="mypassword">  
    3.     <connection-arg name="user" value ="myuser"/>  
    4.     <connection-arg name="password" value ="mypassword"/>  
    5.     <connection-arg name="somearg" value ="someargvalue"/>  
    6.   </drivermanager-connection>  
    7. </database-reference>  

    注意:drivermanager-connection中的user和password属性必须填写,即使增加了connection-arg参数也不行。所以实际上connection-arg的user和password是不需要写的。这点我觉得esper做的不够人性化。

    d.其他关于数据库连接的配置

    下面是一些和数据库交互的配置,更多配置可参考Javadoc

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <database-reference name="mydb2">  
    2. ... configure data source or driver manager settings...  
    3.   <connection-settings auto-commit="true" catalog="mycatalog" read-only="true" transaction-isolation="1" />  
    4. </database-reference>  

    下面是关于连接的生命周期的配置

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <database-reference name="mydb2">  
    2. ... configure data source or driver manager settings...  
    3.     <connection-lifecycle value="pooled"/><!-- retain -->  
    4. </database-reference>  

    如果参数值为pooled,当配置了连接池,则会将每次获取的连接还给连接池。若没配置连接池,则每次获取的连接用完后就关闭。

    如果参数值为retain,则会将连接缓存到esper引擎中,这个epl用完后,另一个epl可以接着用

    2.查询结果缓存策略
    EPL和数据库交互时会产生查询结果,所以引擎若能缓存查询结果将大大提高执行效率,因此esper提供了两种缓存模式。

    a.LRU Cache
    LRU即least-recently-used,中文释义为“最近最少使用”,学过OS的应该知道内存缓存策略里也有这个算法,不明白的请自行搜索。配置如下:

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <database-reference name="mydb">  
    2. ... configure data source or driver manager settings...  
    3.     <lru-cache size="1000"/>  
    4. </database-reference>  

    size的参数值表示最多能缓存多少条查询结果,而不是大小

    b.Expiry-time Cache
    该策略为每一次的查询结果设置了存活期(注意不是每一条查询结果),并且定期清理过期的查询结果。配置如下:

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <database-reference name="mydb">  
    2. ... configure data source or driver manager settings...  
    3.     <expiry-time-cache max-age-seconds="60" purge-interval-seconds="120" ref-type="soft"/>  
    4. </database-reference>  

    max-age-seconds表示存活时间,purge-interval-seconds表示每隔多久清理一次过期的内容,两者单位都是秒。
    ref-type有三个参数值:weak,soft,hard。表示查询结果的引用级别,JVM垃圾回收的时候,会根据此参数决定何时释放缓存。具体解释如下:
    1).weak表示弱引用,JVM在垃圾回收的时候将清除所有的缓存,释放内存。
    2).soft表示软引用,JVM在垃圾回收的时候,只有当所有的弱引用都被回收了才会清除所有的缓存并释放空间。
    3).hard表示强引用,JVM的垃圾回收不会清除缓存,所以引擎将按照规定的存活期和清理时间管理缓存。

    3.Column Change Case
    通常情况下,表字段是大小写不敏感的,但是也有设置为小写敏感的情况,我们可以通过设置使得查询返回的列结果为大写或者小写。配置如下:

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <column-change-case value="lowercase"/>  

    4.SQL Types Mapping
    默认的数据库字段类型映射可以满足需求,不过想修改也是可以的。配置如下:

    [html] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <sql-types-mapping sql-type="2" java-type="int" />  

    sql-type表示数据库字段类型,这里的2映射了具体类型,可在java.sql.Types类中查到,并且这个类里包含了大部分的数据库字段类型。java-type表示对应的java数据类型,大小写不敏感。

    以上就是EPL和数据库交互的相关配置,下面来讲解EPL是怎么和数据库交互的。

    EPL和数据库交互有两个前提,一是JDBC驱动能够预编译sql,而是JDBC驱动能获取数据库的元数据。

    5.Joining SQL Query Results
    通常我们想要的一种交互方式是:输入某个事件到引擎,然后引擎把事件的某个属性作为sql的查询条件交给JDBC驱动,执行sql。正好esper为此提供了相应的解决办法,参看语法:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. sql:database_name [" parameterized_sql_query "]  

    sql是关键字不可少,parameterized_sql_query为sql语句,只与具体的DB有关,无关esper,所以数据库的那些个函数都可以用。先看一个简单的例子:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. select custId, cust_name from CustomerCallEvent, sql:MyCustomerDB [' select cust_name from Customer where cust_id = ${custId} ']  

    引擎接收CustomerCallEvent事件,将事件的custId属性作为查询值,执行MyCustomerDB数据库的Customer表,其中查询条件为Customer的cust_id字段值存在,然后返回相应的custId属性值和cust_name字段值给监听器

    该语法有几点需要注意:
    a.sql需要用单引号或者双引号引起来,然后再用方括号括起来。
    b.${expression}中可以是事件属性,可以是变量、常量等,也可以是用户自定义的函数。例如:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. select * from LimitEvent le,  
    2.   sql:MyCustomerDB [' select cust_name from Customer where  
    3.       amount > ${max(varLowerLimit, MyLib.getLimit(le))} ']  

    c.join的事件可以使用view,但是sql不可使用。不过可以将sql的查询结果通过insert into输出到另外的事件,然后再使用view。例如:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. select customerId, customerName from CustomerCallEvent.win:time(30 sec) as cce,  
    2.   sql:MyCustomerDB ["select cust_id as customerId, cust_name as customerName  
    3.     from Customer  
    4.   where cust_id = ${cce.custId}"] as cq  

    d.可以用as为表的字段设置别名,例如:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. select custId, custName from CustomerCallEvent, sql:MyCustomerDB [' select cust_name as custName from Customer where cust_id = ${custId} ']  

    e.当使用事件的属性作为查询值是,属性名不要和字段名重名,否则会报错,esper无法识别
    f.join的sql语句没有限制,并且可以使用where子句。例如:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. select symbol, symbolDesc from OrderEvent as orders,  
    2.   sql:My_Oracle_DB ['select symbolDesc from SymbolReference'] as reference,  
    3.   sql:My_MySQL_DB ['select orderList from orderHistory'] as history  
    4.     where reference.symbol = orders.symbol  
    5.     and history.symbol = orders.symbol  


    除了普通的join,EPL也支持outer join sql语句,语法也没有什么改变。例如:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. select custId, custName from  
    2.   CustomerCallEvent as cce  
    3.     left outer join  
    4.   sql:MyCustomerDB ["select cust_id, cust_name as custName from Customer where cust_id = ${cce.custId}"] as cq  
    5.     on cce.custId = cq.cust_id  


    6.Using Patterns to Request Data
    除了通过传递外部数据查询数据库,也可以用pattern定时或者以固定频率查询数据库。例如:

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. insert into NewOrders  
    2. select orderId, orderAmount from  
    3.   pattern [every timer:interval(5 sec)],  
    4.     sql:MyCustomerDB ['select orderId, orderAmount from NewOrders']  

    pattern语法之后再说,这里只让大家知道有这么一个用法。

    7.Polling SQL Queries via API
    Esper提供了API直接执行EPL来达到访问数据库的目的。请看下面的代码:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. package example;  
    2.   
    3. import com.espertech.esper.client.Configuration;  
    4. import com.espertech.esper.client.EPAdministrator;  
    5. import com.espertech.esper.client.EPRuntime;  
    6. import com.espertech.esper.client.EPServiceProvider;  
    7. import com.espertech.esper.client.EPServiceProviderManager;  
    8. import com.espertech.esper.client.EPStatement;  
    9. import com.espertech.esper.client.EventBean;  
    10.   
    11. import java.util.Iterator;  
    12.   
    13. /** 
    14.  * Created by Luonanqin on 4/17/14. 
    15.  */  
    16. public class IteratorSQLTest {  
    17.   
    18.     public static void main(String[] args) throws InterruptedException {  
    19.         Configuration config = new Configuration();  
    20.         config.configure("esper.examples.cfg.xml");  
    21.         config.addVariable("vari", Integer.class, 1);  
    22.         EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(config);  
    23.   
    24.         EPAdministrator admin = epService.getEPAdministrator();  
    25.         EPRuntime runtime = epService.getEPRuntime();  
    26.         // id=1, name="luonq"  
    27.         String epl1 = "select id, name from sql:test['select id, name from test1 where id=${vari}']";  
    28.   
    29.         EPStatement state = admin.createEPL(epl1);  
    30.   
    31.         Iterator<EventBean> iter = state.iterator(); // 也可以调用safeIterator方法,该方法以线程安全方式查询DB  
    32.         while (iter.hasNext()) {  
    33.             EventBean eventBean = iter.next();  
    34.             System.out.println(eventBean.get("id") + " " + eventBean.get("name"));  
    35.         }  
    36.     }  
    37. }  

    执行结果:

    [plain] view plaincopy
     
    1. 1 luonq  



    8.SQL Input Parameter and Column Output Conversion
    刚才数据库配置里面有说到可以修改数据库字段类型和java数据类型的映射关系,但是那只是针对全局的设置,如果想针对EPL来设置映射关系,可以实现SQLColumnTypeConversion接口,然后通过注解Hook调用实现类。具体代码及解释如下:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. import com.espertech.esper.client.hook.SQLColumnTypeContext;  
    2. import com.espertech.esper.client.hook.SQLColumnTypeConversion;  
    3. import com.espertech.esper.client.hook.SQLColumnValueContext;  
    4. import com.espertech.esper.client.hook.SQLInputParameterContext;  
    5.   
    6. /** 
    7.  *  
    8.  * MySQLColumnTypeConvertor必须为public类,不然无法实例化。 
    9.  * Esper会为每一个EPL实例,即EPStatement提供一个Convertor实例 
    10.  * 
    11.  * 该例子没有做任何转换。 
    12.  * Created by Luonanqin on 2/9/14. 
    13.  */  
    14. public class MySQLColumnTypeConvertor implements SQLColumnTypeConversion{  
    15.   
    16.     // 转换列的类型  
    17.     public Class getColumnType(SQLColumnTypeContext context) {  
    18.         Class clazz = context.getColumnClassType();  
    19.         return clazz;  
    20.     }  
    21.   
    22.     // 转换列的值  
    23.     public Object getColumnValue(SQLColumnValueContext context) {  
    24.         Object obj = context.getColumnValue();  
    25.         return obj;  
    26.     }  
    27.   
    28.     // 转换传入的参数值  
    29.     public Object getParameterValue(SQLInputParameterContext context) {  
    30.         Object obj = context.getParameterValue();  
    31.         return obj;  
    32.     }  
    33. }  
    34.   
    35. package example;  
    36.   
    37. import com.espertech.esper.client.Configuration;  
    38. import com.espertech.esper.client.EPAdministrator;  
    39. import com.espertech.esper.client.EPServiceProvider;  
    40. import com.espertech.esper.client.EPServiceProviderManager;  
    41. import com.espertech.esper.client.EPStatement;  
    42. import com.espertech.esper.client.EventBean;  
    43.   
    44. import java.util.Iterator;  
    45.   
    46. /** 
    47.  * MySQLColumnTypeConvertor必须为public类,不然无法实例化。 Esper会为每一个EPL提供一个Convertor实例 
    48.  *  
    49.  * Created by Luonanqin on 2/9/14. 
    50.  */  
    51. public class SQLColumnTypeConversionTest {  
    52.   
    53.     public static void main(String[] args) throws InterruptedException {  
    54.         Configuration config = new Configuration();  
    55.         config.configure("esper.examples.cfg.xml");  
    56.         config.addVariable("vari", Integer.class, 1);  
    57.         EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(config);  
    58.   
    59.         EPAdministrator admin = epService.getEPAdministrator();  
    60.         // id=1, name="luonq"  
    61.         String epl1 = "@Hook(type=HookType.SQLCOL, hook='" + MySQLColumnTypeConvertor.class.getName()  
    62.                 + "')select id, name from sql:test['select id, name from test1 where id=${vari}']";  
    63.         System.out.println(epl1);  
    64.         EPStatement state1 = admin.createEPL(epl1);  
    65.   
    66.         Iterator<EventBean> iter = state1.iterator();  
    67.         while (iter.hasNext()) {  
    68.             EventBean eventBean = iter.next();  
    69.             System.out.println(eventBean.get("id") + " " + eventBean.get("name"));  
    70.         }  
    71.     }  
    72. }  

    执行结果:

    [plain] view plaincopy
     
    1. @Hook(type=HookType.SQLCOL, hook='example.MySQLColumnTypeConvertor')select id, name from sql:test['select id, name from test1 where id=${vari}']  
    2. 1 luonq  

    9.SQL Row POJO Conversion
    刚才说的列类型的转换以及列结果的转换,只是普通的转换。Esper还支持表的查询结果按行转换,比如说转换为POJO,而不像之前那样只能针对每一个字段结果单独进行转换。用法也是通过Hook注解来调用转换类。代码如下:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. import java.sql.ResultSet;  
    2. import java.sql.SQLException;  
    3.   
    4. import com.espertech.esper.client.hook.SQLOutputRowConversion;  
    5. import com.espertech.esper.client.hook.SQLOutputRowTypeContext;  
    6. import com.espertech.esper.client.hook.SQLOutputRowValueContext;  
    7.   
    8. /** 
    9.  * Created by Luonanqin on 2/10/14. 
    10.  */  
    11. public class MySQLOutputRowConvertor implements SQLOutputRowConversion {  
    12.   
    13.         // 每行查询结果转换后的类型  
    14.     public Class getOutputRowType(SQLOutputRowTypeContext context) {  
    15.         return String.class;  
    16.     }  
    17.   
    18.         // 返回转换后的内容  
    19.     public Object getOutputRow(SQLOutputRowValueContext context) {  
    20.         ResultSet result = context.getResultSet();  
    21.         Object obj1 = null;  
    22.         Object obj2 = null;  
    23.         try {  
    24.             obj1 = result.getObject("id");  
    25.             obj2 = result.getObject("name");  
    26.         } catch (SQLException e) {  
    27.             e.printStackTrace();  
    28.         }  
    29.   
    30.         return obj1 + " and " + obj2;  
    31.     }  
    32. }  
    33.   
    34. package example;  
    35.   
    36. import com.espertech.esper.client.Configuration;  
    37. import com.espertech.esper.client.EPAdministrator;  
    38. import com.espertech.esper.client.EPServiceProvider;  
    39. import com.espertech.esper.client.EPServiceProviderManager;  
    40. import com.espertech.esper.client.EPStatement;  
    41. import com.espertech.esper.client.EventBean;  
    42.   
    43. import java.util.Iterator;  
    44.   
    45. /** 
    46.  * MySQLOutputRowConvertovr必须为public类,不然无法实例化。 Esper会为每一个EPL提供一个Convertor实例 
    47.  *  
    48.  * Created by Luonanqin on 2/9/14. 
    49.  */  
    50. public class SQLOutputRowConversionTest {  
    51.   
    52.     public static void main(String[] args) throws InterruptedException {  
    53.         Configuration config = new Configuration();  
    54.         config.configure("esper.examples.cfg.xml");  
    55.         config.addVariable("vari", Integer.class, 1);  
    56.         EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(config);  
    57.   
    58.         EPAdministrator admin = epService.getEPAdministrator();  
    59.         // epl中返回的流必须用“*”表示,不能是之前那样写成id或者name  
    60.         // id=1, name="luonq"  
    61.         String epl1 = "@Hook(type=HookType.SQLROW, hook='" + MySQLOutputRowConvertor.class.getName()  
    62.                 + "')select * from sql:test['select id, name from test1 where id=${vari}']";  
    63.         System.out.println(epl1);  
    64.         EPStatement state1 = admin.createEPL(epl1);  
    65.   
    66.         Iterator<EventBean> iter = state1.iterator();  
    67.         while (iter.hasNext()) {  
    68.             EventBean eventBean = iter.next();  
    69.             System.out.println(eventBean.getUnderlying());  
    70.         }  
    71.     }  
    72. }  

    执行结果:

    [plain] view plaincopy
     
    1. @Hook(type=HookType.SQLROW, hook='example.MySQLOutputRowConvertor')select * from sql:test['select id, name from test1 where id=${vari}']  
    2. 1 and luonq  



    以上就是EPL和数据库交互的内容,针对普通的查询需求来说还是够用的,至于insert,update,delete我没有举例子,各位可以自己试试看可不可行。

  • 相关阅读:
    【python cookbook】【数据结构与算法】4.找到最大或最小的N个元素
    【python cookbook】【数据结构与算法】3.保存最后N个元素
    oracle sql语言模糊查询--通配符like的使用教程
    oracle decode处理被除数为0 的情况
    握草
    oracle if else 判断
    Procdure for wanfo business report
    [转]Oracle 阳历转农历
    阴阳历互相转换方法
    [转]解决crystal report水晶报表在浏览器提示bobj未定义的错误
  • 原文地址:https://www.cnblogs.com/yudar/p/4872646.html
Copyright © 2020-2023  润新知