Eql中文手册
====
一个简单,轻量的数据持久层的框架 可以用于代替ibatis/mybatis
[](https://maven-badges.herokuapp.com/maven-central/com.github.bingoohuang/eql/)
[](http://www.apache.org/licenses/LICENSE-2.0.html)
表示并不喜欢ibatis的XML配置文件...
+ 示例 1:
```xml
```
在ibatis中,哪怕是一个简单的SQL语句,我们都需要添加一个CDATA块将它包起来使用(我常常忘记如何写CDATA,每次还得去查XML CDATA的引用)
这玩意是真的麻烦。并且我认为select也很多余,因为这SQL语句一看就知道这是一个查询语句
+ 示例 2:
当两个或多个SQL在同一个XML文件上工作时,只要有一个无效的SQL,那你就等着报错,然后逐个检查文件中的SQL,再改正
```xml
SELECT 1 FROM DUAL WHERE 1 > 0
```
这就是一颗老鼠屎坏了一锅汤(笑)...
+ 示例 3:
每次编写一个新的ibatis配置文件时,我总是会忘记一些细节(例如:如何创建SQLMap,如何编写规范有效的ibatis.xml)
基本每次都需要从别人那里拷一个ibatis.xml文件来对照
+ 示例 4:
对于动态SQL,我们时常要套一些标签进去,麻烦的一批,你又必须得这么写
```sql
```
+ 示例 5:
老子就是不喜欢XML文件,它是真的麻烦,所以我搞了非常简单的Eql。
#一分钟教程
* 将 [eql-DEFAULT.properties](https://github.com/bingoohuang/eql/blob/master/src/test/resources/eql/eql-DEFAULT.properties) 复制到你的类路径EQL,并对数据库连接信息(如URL,密码和用户名)进行一些更改.
* 创建一个类(com/test/Demo.java)
* 再创建一个.eql文件(com/test/Demo.eql)
* 在类中写入如下代码:
```java
String str = new Eql().selectFirst("demo").execute();
```
* 在.eql文件中写入一个eql
```sql
-- [demo]
select 'X' from dual
```
* 正如你所看到的,非常简单
#只返回一行结果
```java
Map row = new Eql().selectFirst("oneRow").execute();
```
```sql
-- [oneRow]
select 'X', 'Y' from dual
```
#返回一个Javabean
```java
XYBean xy = new Eql().selectFirst("javabean").execute();
```
```sql
-- [javabean returnType=XYBean]
select 'X', 'Y' from dual
```
#返回行数
```java
List xys = new Eql().select("rows").
.returnType(XYBean.class)
.execute();
```
```sql
-- [rows]
select 'X' X, 'Y' Y from dual
union
select 'A' X, 'B' Y from dual
```
#带参数的SQL (参数顺序)
* 示例 1
```java
String x = new Eql().selectFirst("autoSeq1")
.params("x")
.execute();
```
```sql
-- [autoSeq1]
select 'X' from dual
where 'x' = '##'
```
* 示例 2
```java
String x = new Eql().selectFirst("autoSeq2")
.params("x", "y")
.execute();
```
```sql
-- [autoSeq2]
select 'X' from dual
where 'x' = '##'
and 'y' = '##'
```
* 示例 3
```java
String x = new Eql().selectFirst("autoSeq3")
.params("y", "x")
.execute();
```
```sql
-- [autoSeq3]
select 'X' from dual
where 'x' = '#2#'
and 'y' = '#1#'
```
#带参数的SQL (参数名称)
* 示例 1
```java
String x = new Eql().selectFirst("bean")
.params(new XyBean("x", "y"))
.execute();
```
```sql
-- [bean]
select 'X' from dual
where 'x' = '#x#'
and 'y' = '#y#'
```
* 示例 2
```java
String x = new Eql().selectFirst("map")
.params(mapOf("x", "a", "y", "b"))
.execute();
```
```sql
-- [map]
select 'X' from dual
where 'a' = '#x#'
and 'b' = '#y#'
```
* 示例 3
```java
String x = new Eql().selectFirst("map")
.params("a", "b")
.execute();
```
```sql
-- [map]
select 'X' from dual
where 'a' = '#_1#'
and 'b' = '#_2#'
```
在示例3中,我们使用了_1和_2,我叫它们为内置参数。
更多的内置参数列表:
1. `_time` 当前时间,类型: `java.sql.Timestamp`
2. `_date` 当前日期,类型:`java.util.Date`
3. `_host` 当前主机名
4. `_ip` 当前IP
5. `_params` 当前params数组
6. `_paramsCount` 当前params数组长度
7. `_1`,`_2`,`_3`,... prams序列
8. `_dynamics` 当前动态数组
9. `_dynamicsCount` 当前动态数组长度
10. `_databaseId`, 对应的数据库:oracle/mysql/h2/db2/sqlserver
# 动态SQL
Eql的动态SQL主要基于 [OGNL](http://commons.apache.org/proper/commons-ognl/) 的表达式.
## if
```java
// 假如这条SQL是:
// select 'X' from dual where 'a' = ?
// 这条SQL将带一个参数 'a'
String x = new Eql().selectFirst("ifDemo")
.params(mapOf("x", "a"))
.execute();
// 假如这条SQL是:
// select 'X' from dual
String y = new Eql().selectFirst("ifDemo")
.params(mapOf("x", "b"))
.execute();
```
```sql
-- [ifDemo]
select 'X' from dual
-- if x == "a"
where 'a' = '#x#'
-- end
-- [ifDemo2]
select 'X' from dual
-- if x == "a"
where 'a' = '#x#'
-- else if x == "b"
where 'b' = '#x#'
-- else
where 'c' = '##'
-- end
-- 或者使用更加紧凑的语法
-- [ifDemo]
select 'X' from dual /* if x == "a" */ where 'a' = '#x#' /* end */
```
## iff
```sql
-- [ifDemo]
select 'X' from dual
-- iff x == "a"
where 'a' = '#x#'
-- or use more compact syntax
-- [ifDemo]
select 'X' from dual /* iff x == "a" */ where 'a' = '#x#'
```
使用 static 字段:
```java
public class OgnlStaticTest {
public static String STATE = "102";
@Test
public void test() {
String str = new Eql("mysql").id("ognlStatic").limit(1)
.params(ImmutableMap.of("state", "102", "x", "y"))
.execute();
assertThat(str, is(nullValue()));
str = new Eql("mysql").id("ognlStatic").limit(1)
.params(ImmutableMap.of("state", "103", "x", "x"))
.execute();
assertThat(str, is("X"));
}
}
```
```sql
-- [ognlStatic]
select 'X'
from DUAL
-- iff state == @org.n3r.eql.OgnlStaticTest@STATE
where 'x' = '#x#'
```
## switch
```sql
-- [switchSelect returnType=org.n3r.eql.SimpleTest$Bean]
SELECT A,B,C,D,E
FROM EQL_TEST
WHERE
-- switch a
-- case 1
A = 1
-- case 2
A = 2
-- end
-- 或者使用默认关键字
-- [switchSelectWithDefault returnType=org.n3r.eql.SimpleTest$Bean]
SELECT A,B,C,D,E
FROM eql_TEST
WHERE
-- switch a
-- case 1
A = 1
-- case 2
A = 2
-- default
A = 3
-- end
```
## for
```java
Map map = Maps.newHashMap();
map.put("list", ImmutableList.of("a", "b", "x"));
String str = new Eql().selectFirst("for1").params(map).execute();
assertThat(str, is("x"));
```
```sql
-- [for1]
SELECT 'x'
FROM DUAL
WHERE 'x' in
-- for item=item index=index collection=list open=( separator=, close=)
#item#
-- end
```
## is(Not)Null/is(Not)Empty/is(Not)Blank
```sql
-- [isEmpty]
SELECT B
FROM eql_TEST
-- isEmpty a
WHERE A in (1,2)
-- else
WHERE A in (3,4)
-- end
-- [isNotEmpty]
SELECT B
FROM eql_TEST
-- isNotEmpty a
WHERE A = '#a#'
-- end
```
## in
```sql
SELECT NAME FROM EQL_IN WHERE ID IN (/* in _1 */)
```
```java
List names = new Eql().params(Lists.newArrayList("1", "2")).execute();
```
## trim
```sql
-- [updateAuthor]
update author
-- trim prefix=SET suffixOverrides=,
-- iff username != null
username='#username#',
-- iff password != null
PASSWORD='#password#',
-- iff email != null
email='#email#',
-- iff bio != null
bio='#bio#',
-- end
where id='#id#'
-- [selectBlog]
SELECT STATE FROM BLOG
-- trim prefix=WHERE prefixOverrides=AND|OR
-- iff state != null
state = '#state#'
-- iff title != null
AND title like '#title#'
-- iff author != null and author.name != null
AND author_name like '#author.name#'
-- end
GROUP BY STATE
```
# 分页支持
```java
EqlPage page = new EqlPage(3, 2);
List beans = new Eql().id("testPage")
.returnType(SimpleTest.Bean.class)
.limit(page)
.params("DC")
.execute();
assertThat(page.getTotalRows(), is(10));
```
```sql
-- [testPage]
SELECT A,B,C,D,E
FROM eql_TEST
WHERE C = '##'
```
# 动态的表名
```java
String str = new Eql().selectFirst("replace1")
.params("x").dynamics("DUAL").execute();
assertThat(str, is("x"));
str = new Eql().selectFirst("replace2").params("x")
.dynamics(ImmutableMap.of("table", "DUAL")).execute();
assertThat(str, is("x"));
```
```sql
-- [replace1]
SELECT 'x'
FROM $$
WHERE 'x' = '##'
-- [replace2]
SELECT 'x'
FROM $table$
WHERE 'x' = '##'
```
# 批处理
```java
Eql eql = new Eql();
eql.startBatch(/*batchSize*/10);
for (int i = 0; i < 10; ++i) {
String orderNo = randLetters(10);
String userId = randLetters(10);
int prizeItem = randInt(10);
int ret = eql.insert("insertPrizeBingoo")
.params(orderNo, "Olympic", "" + prizeItem, userId)
.execute();
assertEquals(0, ret);
}
eql.executeBatch();
```
```sql
-- [insertPrizeBingoo]
INSERT INTO EQL_TEST_BINGOO(ORDER_NO, ACTIVITY_ID, ITEM_ID, USER_ID, BINGOO_TIME)
VALUES(##, ##, ##, ##, SYSDATE)
```
# 选项支持
```sql
-- [likeDemo]
select 'x' from demo where name like '#:Like#'
-- [leftLikeDemo]
select 'x' from demo where name like '#:LeftLike#'
-- [rightLikeDemo]
select 'x' from demo where name like '#:RightLike#'
```
```java
new Eql().id("likeDemo").params("b").execute();
// 16:12:51.316 [main] DEBUG org.n3r.eql.Eql - prepare sql likeDemo: select 'x' from demo where name like ?
// 16:12:51.317 [main] DEBUG org.n3r.eql.map.EqlRun - param: [%b%]
new Eql().id("leftLikeDemo").params("c").execute();
// 16:12:51.326 [main] DEBUG org.n3r.eql.Eql - prepare sql leftLikeDemo: select 'x' from demo where name like ?
// 16:12:51.326 [main] DEBUG org.n3r.eql.map.EqlRun - param: [%c]
new Eql().id("rightLikeDemo").params("a").execute();
// 16:12:51.331 [main] DEBUG org.n3r.eql.Eql - prepare sql rightLikeDemo: select 'x' from demo where name like ?
// 16:12:51.331 [main] DEBUG org.n3r.eql.map.EqlRun - param: [a%]
```
# Oracle Blob 支持
```java
public void testOracleBlob() {
new Eqll().id("insertBlob").params("中华人民共和国").execute();
byte[] bytes = new Eqll().id("selectBlob").limit(1).execute();
assertThat(new String(bytes, Charsets.UTF_8), is("中华人民共和国"));
String ret = new Eqll().id("selectBlobString").limit(1).execute();
assertThat(ret, is("中华人民共和国"));
AsResult asResult = new Eqll().id("selectBlobAsResult").limit(1).execute();
assertThat(asResult.getSeq(), is(1));
assertThat(asResult.getRemark(), is("中华人民共和国"));
Integer effectedRows = new Eqll().id("updateBlob").params("台湾省").execute();
assertThat(effectedRows, is(1));
ret = new Eqll().id("selectBlobString").limit(1).execute();
assertThat(ret, is("台湾省"));
}
public static class AsResult {
private String state;
private String remark;
private int seq;
// setters ang getters
}
```
```sql
-- [insertBlob onerr=resume]
DROP TABLE eql_BLOB;
CREATE TABLE eql_BLOB (BOB BLOB);
INSERT INTO eql_BLOB(BOB) VALUES(#:LOB#)
-- [selectBlob]
SELECT BOB FROM eql_BLOB
-- [selectBlobString returnType=string]
SELECT BOB FROM eql_BLOB
-- [selectBlobAsResult returnType=org.n3r.eql.JavaBlobTest$AsResult]
SELECT 1 as seq, BOB as remark FROM eql_BLOB
-- [updateBlob]
UPDATE eql_BLOB SET BOB = '#:LOB#'
```
# [Diamond-miner](https://github.com/bingoohuang/diamond-miner) 支持示例
Eql 也可以从 Diamond 进行加载
首先在类路径上创建 eql/eql-diamond.properties 如:
```
sql.resource.loader=org.n3r.eql.diamond.DiamondEqlResourceLoader
transactionType=jdbc
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
user=orcl
password=orcl
```
正如你看到的,我们将SQL资源加载器重新定义为diamond-specific。
然后在 diamond 中添加一个配置:
```
group=EQL
dataId=org.n3r.eql.DiamondTest.eql
content=
-- [diamondDemo]
SELECT 'Hello' FROM DUAL
```
然后在Java代码中
```java
String str = new Eql("diamond").selectFirst("diamondDemo").execute();
System.out.println(str);
```
# 使用JDBC语句进行多次 select/update
```java
Eql eql = new Eql().id("selectStmt");
ESelectStmt selectStmt = eql.selectStmt();
selectStmt.executeQuery(3);
String str = selectStmt.next();
assertThat(str, is("CC"));
assertThat(selectStmt.next(), is(nullValue()));
selectStmt.executeQuery(4);
str = selectStmt.next();
assertThat(str, is("DC"));
assertThat(selectStmt.next(), is(nullValue()));
selectStmt.close();
eql.close();
```
```java
Eql eql = new Eql().id("updateStmt");
EUpdateStmt updateStmt = eql.updateStmt();
int rows = updateStmt.update(3, "Bingoo");
assertThat(rows, is(1));
rows = updateStmt.update(4, "Dingoo");
assertThat(rows, is(1));
updateStmt.close();
eql.close();
```
```sql
-- [selectStmt]
SELECT C
FROM eql_TEST
WHERE A = '##'
-- [updateStmt]
UPDATE eql_TEST
SET C = '#2#'
WHERE A = '#1#'
```
# 自定义结果映射器 示例:
```java
import org.n3r.eql.map.EqlRowMapper;
public static class MyMapper implements EqlRowMapper {
private String name;
@Override
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
name = rs.getString(1);
return null;
}
public String getName() {
return name;
}
}
@Test
public void test() {
MyMapper myMapper = new MyMapper();
new Eql().returnType(myMapper).execute("SELECT 'X' FROM DUAL");
assertThat(myMapper.getName(), is("X"));
}
```
# 自定义配置支持
```java
Eqll.choose(new EqlJdbcConfig("oracle.jdbc.driver.OracleDriver",
"jdbc:oracle:thin:@127.0.0.1:1521:orcl", "orcl", "orcl"));
Timestamp ts = new Eqll().limit(1).execute("SELECT SYSDATE FROM DUAL");
// or
Eqll.choose(new EqlPropertiesConfig(
EqlConfigKeys.DRIVER + "=oracle.jdbc.driver.OracleDriver\n" +
EqlConfigKeys.URL + "=jdbc:oracle:thin:@127.0.0.1:1521:orcl\n" +
EqlConfigKeys.USER + "=orcl\n" +
EqlConfigKeys.PASSWORD + "=orcl\n"));
```
支持的配置如下所示:
## **connection.impl**
+ 含义:用于实现 `org.n3r.eql.trans.EqlConnection` 接口。
+ 默认值:当设置 jndiName 时, 应使用 `org.n3r.eql.trans.EqlJndiConnection`,否则 `org.n3r.eql.trans.EqlSimpleConnection`。
+ 比如:`org.n3r.eql.trans.EqlC3p0Connection` 或者你自定义实现。
## **jndiName**
+ 含义:指定的 JNDI 名称,用于使用 JNDI 的数据源。
+ 默认值:N/A。
+ 比如:N/A。
## **java.naming.factory.initial**
+ 含义:与 **jndiName** 一起使用。
+ 默认值:无。
+ 比如:`weblogic.jndi.WLInitialContextFactory`
## **java.naming.provider.url**
+ 含义:与 **jndiName** 一起使用。
+ 默认值:无。
+ 比如: `t3://127.0.0.1:7001/`
## **transactionType**
+ 含义:与 **jndiName** 一起使用。
+ 默认值:JDBC。
+ 比如:JTA。
## **driver**
+ 含义:JDBC驱动程序名称。
+ 默认值:无。
+ 比如:`oracle.jdbc.driver.OracleDriver`, `com.mysql.jdbc.Driver`,等...
## **url**
+ 含义:JDBC的url
+ 默认值:无。
+ 比如:`jdbc:oracle:thin:@127.0.0.1:1521:orcl`,`jdbc:mysql://localhost:3306/diamond?useUnicode=true&&characterEncoding=UTF-8&connectTimeout=1000&autoReconnect=true`
## **user**
+ 含义:JDBC的用户名。
+ 默认值:无。
+ 比如:orcl。
## **password**
+ 含义:JDBC的密码。
+ 默认值:无。
+ 比如:orcl。
## **expression.evaluator**
+ 含义:类全名 `org.n3r.eql.base.ExpressionEvaluator`。
+ 默认值:`org.n3r.eql.impl.OgnlEvaluator`。
+ 比如:看情况。
## **sql.resource.loader**
+ 含义:EQL资源加载器,FQCN实现 `org.n3r.eql.base.EqlResourceLoader`。
+ 默认值:`org.n3r.eql.impl.FileEqlResourceLoader` 它用于读取EQL
+ 比如:`org.n3r.eql.diamond.DiamondEqlResourceLoader` 或者自定义实现。
## **dynamic.language.driver**
+ 含义:EQL的动态支持语言驱动,FQCN实现 `org.n3r.eql.base.DynamicLanguageDriver`。
+ 默认值:`org.n3r.eql.impl.DefaultDynamicLanguageDriver` 使用SQL特殊注释来实现动态SQL。
+ 比如:`org.n3r.eql.impl.FreemarkerDynamicLanguageDriver` 或自定义实现。
## **sql.parse.lazy**
+ 含义:执行时解析动态EQL。
+ 默认值:false。
+ 比如:true or yes.
# 集成 [diamond-client](https://github.com/bingoohuang/diamond-miner)
## 从 diamond 读取连接配置
* 添加 diamond :
```
group=EqlConfig,dataId=DEFAULT,content=
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
user=orcl
password=orcl
```
* 使用Dql替代Eql来处理 diamond 连接配置:
```java
// read diamand content of group=EqlConfig, dataId=DEFAULT as connection config
new Dql().id("xxx").execute();
// read diamond content of group=EqlConfig, dataId=DSMALL as connection config
new Dql("DSMALL").id("yyy").execute();
```
## 从 diamond 中读取Eql
在连接配置中, 设置 `sql.resource.loader` 为 `org.n3r.eql.diamond.DiamondEqlResourceLoader`。
```java
package org.n3r.eql;
public class DiamondTest {
@Test
public void test1() throws InterruptedException {
// Will read diamond content of group=EQL,dataId=org.n3r.eql.DiamondTest.eql
// The diamond content can have the same structure with normal eql file.
String str = new Eql("diamond").selectFirst("diamondDemo").execute();
System.out.println(str);
}
}
```
## 缓存SQL查询结果
SQL查询结果缓存可以通过SQL选项中的cache关键字启用:
```sql
-- [test1 cache]
SELECT TO_CHAR(SYSTIMESTAMP, 'HH24:MI:SS.FF6') FROM DUAL
```
默认的缓存模型是基于guava缓存,这些缓存将在一天后过期。
如果要替代缓存模型,可以这么写入:
```sql
-- global settings cacheModel.impl.myCache=@org.n3r.eql.cache.GuavaCacheProvider("expireAfterWrite=3s,maximumSize=1000")
-- [test1 cache cacheModel=myCache]
SELECT TO_CHAR(SYSTIMESTAMP, 'HH24:MI:SS.FF6') FROM DUAL
```
该类 `org.n3r.eql.cache.GuavaCacheProvider` 由Eql提供,其缓存构建器的规范与guava的[缓存规范](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/cache/CacheBuilderSpec.html)相同。
自定义缓存所提供程序类应实现 `org.n3r.eql.cache.EqlCacheProvider` 并可选的实现 `org.n3r.eql.spec.ParamsAppliable` 在有参数设置时。
# 支持简单的基于POJO的注解配置
**@EqlTable**
指定与该类相关的表名。
如果该类没有被 @EqlTable 注解,那么默认的表名将会自动进行如下转换。
Person to person(转为小写), PersonInfo to person_info(第二个单词前加_)。
**@EqlId**
指定该字段是否是表中的主键。
如果属性名称为 **id**, 那么它也会被视为隐性的 @EqlId.
**@EqlColumn**
指定与普通字段名称。
当使用 @EqlColumn 却未注解时,默认名称将从propertyName转换成加_(下划线)和全小写。
比如:personName to person_name。
**@EqlSkip**
跳过映射表的字段。
**CRUD**
更新和删除API并将使用id作为其条件。
读取API将所有的非空字段作为其组合条件。
```java
@EqlTable(name = "personx")
public static class Person2 {
@EqlId
@EqlColumn(name = "id")
private String pid;
@EqlColumn(name = "name")
private String pname;
private Integer age;
@EqlSkip
private String remark;
// getters and setters
}
@Test
public void testAnnotation() {
Person2 person = new Person2();
person.setPid("1002");
person.setPname("bingoo");
person.setAge(30);
// delete from person where id = ?
new Pql("mysql").delete(person);
// insert into person(id,name,age) values(?,?,?)
new Pql("mysql").create(person);
person.setPname("huang");
person.setAge(null);
// update person set age = ? where id = ?
int effectedRows = new Pql("mysql").update(person);
assertThat(effectedRows, is(1));
Person2 queryPerson = new Person2();
queryPerson.setPid("1002");
// select id,name,age from person where id = ?
List resultPerson = new Pql("mysql").read(queryPerson);
assertThat(resultPerson.size(), is(1));
effectedRows = new Pql("mysql").delete(queryPerson);
assertThat(effectedRows, is(1));
}
```
# Eqler
为了简化Eql API的使用,这里介绍了Eqler。
Eqler是一个接口,其中使用这些方法来执行SQL和用于获取进程的结果
Eqler实例由EqlerFactory创建
以下是示例:
``` java
package org.n3r.eql.eqler.crud;
import org.n3r.eql.eqler.annotations.EqlerConfig;
import org.n3r.eql.eqler.annotations.Sql;
import org.n3r.eql.eqler.annotations.SqlId;
import java.util.List;
import java.util.Map;
@EqlerConfig("mysql")
public interface StudentEqler {
void prepareData();
int addStudent(int studentId, String name, int age);
@Sql("insert into eql_student values(#studentId#, #name#, #age#)")
int addStudent(Student student);
@Sql("insert into eql_student values(#a#, #b#, #c#)")
int addStudentAnotherWay(@NamedParam("a") int studentId, @NamedParam("b") String name, @NamedParam("c") int age);
@Sql("select * from eql_student")
List queryAllStudents();
String queryStudentName(int studentId);
@SqlId("queryStudent")
Map queryStudentMap(int studentId);
Student queryStudent(int studentId);
}
```
```sql
-- org/n3r/eql/eqler/crud/StudentEqler.eql
-- [prepareData]
drop table if exists eql_student;
create table eql_student(student_id int, name varchar(10), age int);
-- [addStudent]
insert into eql_student
values('##', '##', '##')
-- [queryStudentName]
select name from eql_student where student_id = '##'
-- [queryStudent]
select student_id, name, age from eql_student where student_id = '##'
```
```java
@Test
public void test() {
StudentEqler eqler = EqlerFactory.getEqler(StudentEqler.class);
eqler.prepareData();
eqler.addStudent(1, "bingoo", 123);
eqler.addStudent(new Student(2, "huang", 124));
eqler.addStudentAnotherWay(3, "dingoo", 125);
List students = eqler.queryAllStudents();
assertThat(students.toString(), is(equalTo(
"[Student{studentId=1, name='bingoo', age=123}, " +
"Student{studentId=2, name='huang', age=124}, " +
"Student{studentId=3, name='dingoo', age=125}]"
)));
Student student1 = eqler.queryStudent(1);
assertThat(student1.toString(), is(equalTo("Student{studentId=1, name='bingoo', age=123}")));
Map student2 = eqler.queryStudentMap(2);
assertThat(student2.toString(), is(equalTo("{age=124, name=huang, student_id=2}")));
String studentName = eqler.queryStudentName(1);
assertThat(studentName, is(equalTo("bingoo")));
}
```
# TODO
+ 内联注释,比如:`/* iff ... */` 是正则表达式的解析,并且该方法不会 `/* ... */` 在字符串中忽略,比如:`'literal string /* if xxx */'`。
# 常见问题解答
## MySQLNonTransientConnectionException
```
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Communications link failure during rollback(). Transaction resolution unknown.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:377)
at com.mysql.jdbc.Util.getInstance(Util.java:360)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:935)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:924)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:870)
at com.mysql.jdbc.ConnectionImpl.rollback(ConnectionImpl.java:4606)
at org.n3r.eql.trans.SimpleDataSource.popConnection(SimpleDataSource.java:638)
at org.n3r.eql.trans.SimpleDataSource.getConnection(SimpleDataSource.java:207)
at org.n3r.eql.trans.EqlSimpleConnection.getConnection(EqlSimpleConnection.java:17)
at org.n3r.eql.trans.EqlJdbcTran.getConn(EqlJdbcTran.java:58)
at org.n3r.eql.Eql.createConn(Eql.java:105)
at org.n3r.eql.Eql.execute(Eql.java:157)
```
你可以试着使用,`jdbc:mysql://192.168.99.100:13306/dba?useUnicode=true&&characterEncoding=UTF-8&connectTimeout=3000&socketTimeout=3000&autoReconnect=true` 而不是 `jdbc:mysql://192.168.99.100:13306/dba`
# 常见问题解答
## java.lang.NullPointerException
像int/long/short这样的一个原始的返回类型会导致NPE没有任何行。
在这种情况下,应该使用像Integer/Long/Short这样的相关包装类型。
```java
@Test
public void returnInteger() {
Integer intValue = new Eql("h2").limit(1)
.returnType(Integer.class).execute("select 1 where 2 > 3");
assertThat(intValue).isNull();
}
@Test(expected = NullPointerException.class)
public void returnInt() {
int intValue = new Eql("h2").limit(1)
.returnType(int.class).execute("select 1 where 2 > 3");
}
```
# 搬运工人
## mysql
运行mysql:
`docker run -p 13306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql`
运行客户端:
`docker run -it --rm mysql mysql -h192.168.99.100 -uroot -P13306 -pmy-secret-pw`
# OGNL 相关知识
eql默认使用OGNL表达式来做动态条件SQL的判断,OGNL表达式可以参见[ognl language guide](https://commons.apache.org/proper/commons-ognl/language-guide.html).
## 注意项
`'a'` 表示字符a,要表示字符串a,需要使用双引号`"a"`;
`'ab'`和`"ab""` 都可以表示字符串ab。
## OGNL有以下几种常数:
1. String类型,如:在Java中由单引号或双引号分隔,全部字符转义。
2. Character类型,如:在Java中由单引号分隔,还有全套转义。
3. Numeric类型,除了Java有的类型 如:int,longs,float,double之外,OGNL还可以使用"b"或"B"后缀指定BigDecimals,而BigIntegers具有"h"或"H"后缀(认为“巨大” - 我们选择了“Big”,因为它不干扰十六进制数字);
布尔(true和false)文字;
4. Boolean类型 (true 和 false) 。
5. null
如果要在动态SQL中将变量与字符串进行比较,请注意单引号或双引号。
##测试代码:
```java
@SneakyThrows
public static void main(String[] args) {
val map = ImmutableMap.of(
"a", "1",
"b", "11",
"c", 0,
"d", "0");
out.println(Ognl.getValue("a == '1'", map)); // false
out.println(Ognl.getValue("a == 1", map)); // true
out.println(Ognl.getValue("a == \"1\"", map)); // true
out.println(Ognl.getValue("b == '11'", map)); // true
out.println(Ognl.getValue("b == 11", map)); // true
out.println(Ognl.getValue("c == \"0\"", map)); // true
out.println(Ognl.getValue("c == '0'", map)); // false
out.println(Ognl.getValue("c == 0", map)); // true
out.println(Ognl.getValue("d == \"0\"", map)); // true
out.println(Ognl.getValue("d == '0'", map)); // false
out.println(Ognl.getValue("d == 0", map)); // true
}
```