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

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

1.连接数据库

a.JNDI获取连接

配置如下:

  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的文档

使用方法:

  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为例)

  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达到同样的效果。代码如下:

  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. ConfigurationDBRef configDB = new ConfigurationDBRef();
  9. // BasicDataSourceFactory is an Apache DBCP import
  10. configDB.setDataSourceFactory(props, BasicDataSourceFactory.class.getName());
  11. configDB.setConnectionLifecycleEnum(ConfigurationDBRef.ConnectionLifecycleEnum.POOLED);
  12. Configuration configuration = new Configuration();
  13. configuration.addDatabaseReference("mydb3", configDB);

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

  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

  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

  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>

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

  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的应该知道内存缓存策略里也有这个算法,不明白的请自行搜索。配置如下:

  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
该策略为每一次的查询结果设置了存活期(注意不是每一条查询结果),并且定期清理过期的查询结果。配置如下:

  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
通常情况下,表字段是大小写不敏感的,但是也有设置为小写敏感的情况,我们可以通过设置使得查询返回的列结果为大写或者小写。配置如下:

  1. <column-change-case value="lowercase"/>

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

  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为此提供了相应的解决办法,参看语法:

  1. sql:database_name [" parameterized_sql_query "]

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

  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}中可以是事件属性,可以是变量、常量等,也可以是用户自定义的函数。例如:

  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。例如:

  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为表的字段设置别名,例如:

  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子句。例如:

  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语句,语法也没有什么改变。例如:

  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定时或者以固定频率查询数据库。例如:

  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来达到访问数据库的目的。请看下面的代码:

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

执行结果:

  1. 1 luonq

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

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

执行结果:

  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注解来调用转换类。代码如下:

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

执行结果:

  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我没有举例子,各位可以自己试试看可不可行。

Esper学习之十:EPL语法(六)的更多相关文章

  1. Esper学习之七:EPL语法(三)

    1.Aggregation 和SQL一样,EPL也有Aggregation,即聚合函数.语法如下: aggregate_function([all|distinct] expression) aggr ...

  2. Esper学习之五:EPL语法(一)

    上篇说到了Esper的Context,要是不了解的同学请参看<Esper学习之四:Context>,看过的同学如果还是不理解的话可以给我评论,我将会尽可能的解答.之前有些同学问我Conte ...

  3. Esper学习之六:EPL语法(二)

    中秋三天,说闲也不闲,调调工作的代码,倒还解决不少问题.不过也是因为最近工作忙的缘故,Esper被我冷落不少日子了,趁着今天最后一天,赶紧写一篇出来. 从上一篇开始说EPL的语法,主要是关于注解的.今 ...

  4. Esper学习之八:EPL语法(四)

    关于EPL,已经写了三篇了,预估计了一下,除了今天这篇,后面还有5篇左右.大家可别嫌多,官方的文档对EPL的讲解有将近140页,我已经尽量将废话都干掉了,再配合我附上的例子,看我的10篇文章比那140 ...

  5. Esper学习之十二:EPL语法(八)

    今天的内容十分重要,在Esper的应用中是十分常用的功能之一.它是一种事件集合,我们可以对这个集合进行增删查改,所以在复杂的业务场景中我们肯定不会缺少它.它就是Named Window. 由于本篇篇幅 ...

  6. Esper学习之十五:Pattern(二)

    上一篇开始了新一轮语法——Pattern的讲解,一开始为大家普及了几个基础知识,其中有说到操作符.当时只是把它们都列举出来了,所以今天这篇就是专门详解这些操作符的,但是由于篇幅限制,本篇先会讲几个,剩 ...

  7. Esper学习之十四:Pattern(一)

    1. Pattern Atoms and Pattern operatorsPattern是通过原子事件和操作符组合在一起构成模板.原子事件有3类,操作符有4类,具体如下: 原子事件:1). 普通事件 ...

  8. Esper学习之九:EPL语法(五)

    本篇的内容主要包括了Subquery(也就是子查询)和Join,内容不少,但是不难,基本上和sql差不太多. 1.Subquery EPL里的Subquery和sql的类似,是否比sql的用法更多我不 ...

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

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

随机推荐

  1. 多媒体开发之rtmp---rtmp client 编译

    静态库连接编译问题: assert 原来在c编译器下没定义 ceill 没连接没加 -lm http://blog.chinaunix.net/uid-20681545-id-3786786.html ...

  2. 每天一个linux命令:pwd命令

    Linux中用 pwd 命令来查看”当前工作目录“的完整路径. 简单得说,每当你在终端进行操作时,你都会有一个当前工作目录. 在不太确定当前位置时,就会使用pwd来判定当前目录在文件系统内的确切位置. ...

  3. T4 好用的Vs扩展

  4. “A configuration with this name already exists” error in eclipse run configurations

    “A configuration with this name already exists” error in eclipse run configurations Is there a way t ...

  5. XML 中可嵌入 cmd命令脚本

    原文要参照代码 1. XML解析 Task逻辑块可相互组合,形成复杂的树状结构,其结构用XML表示,即写成XML文件的形式. 样例如下: <!-- 顺序执行块 --> <seq> ...

  6. struts实现国际化

    上篇博客学习了Java国际化的一些基础知识,了解了这些知识之后,我们可以更好的过度到struts国际化. 本篇博客主要学习struts国际化的实现: 1.配置环境设置baseName. 在struts ...

  7. SCXcodeSwitchExpander自动填充switch语句下枚举类型case

    下载地址:https://github.com/stefanceriu/SCXcodeSwitchExpander 跟VVDocumenter规范注释生成器的安装方式一样: 下载开源工程在Xcode重 ...

  8. 动态添加的html元素绑定事件的方法

    避免先写了DOM操作,但是元素是动态加载的,所以点击不生效,比较好的方法有两个: 1.动态添加的时候加行内事件,比如onclick="funcName()" 在js中写好方法名对应 ...

  9. Dos命令大全(1)

    MS DOS 命令大全 一.基础命令 1 dir 无参数:查看当前所在目录的文件和文件夹. /s:查看当前目录已经其所有子目录的文件和文件夹. /a:查看包括隐含文件的所有文件. /ah:只显示出隐含 ...

  10. PHP日期知识

    (1)date用法: date(格式,[时间]);如果没有时间参数,则使用当前时间. 格式是一个字符串,其中以下字符有特殊意义:U 替换成从一个起始时间(好象是1970年1月1日)以来的秒数 Y 替换 ...