# FastSQL
**Repository Path**: JiuTianRuanBa/FastSQL
## Basic Information
- **Project Name**: FastSQL
- **Description**: FastSQL一个基于spring-jdbc 的简单 ORM 框架,它支持 sql 构建、sql 执行、命名参数绑定、查询结果自动映射和通用 DAO
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 5
- **Created**: 2022-06-24
- **Last Updated**: 2022-06-24
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 轻量级数据库访问框架FastSQL
[](https://maven-badges.herokuapp.com/maven-central/top.fastsql/fastsql)
[](http://www.apache.org/licenses/LICENSE-2.0.html)

# 目录
- [1 简介](#1-%E7%AE%80%E4%BB%8B)
- [2 入门](#2-%E5%85%A5%E9%97%A8)
- [3 SQLFactory的配置](#3-sqlfactory-%E9%85%8D%E7%BD%AE)
- [4 SQL类作为sql语句构建器](#4-sql%E7%B1%BB%E4%BD%9C%E4%B8%BAsql%E8%AF%AD%E5%8F%A5%E6%9E%84%E5%BB%BA%E5%99%A8%E4%BD%BF%E7%94%A8)
- [5 SQL类的执行sql功能](#5-sql%E7%B1%BB%E7%9A%84%E6%89%A7%E8%A1%8Csql%E5%8A%9F%E8%83%BD)
- [6 BaseDAO](#6-basedao)
- [7 通用工具](#7-%E9%80%9A%E7%94%A8%E5%B7%A5%E5%85%B7)
- [8 配置项](#8-%E9%85%8D%E7%BD%AE%E9%A1%B9)
- [9 其他](#9-%E5%85%B6%E4%BB%96)
- [10 更新日志](#10-更新日志)
# 1 简介
FastSQL一个基于spring-jdbc的简单ORM框架,它支持sql构建、sql执行、命名参数绑定、查询结果自动映射和通用DAO。结合了Hibernate/JPA快速开发和Mybatis高效执行的优点。
FastSQL可以完全满足你控制欲,可以用Java代码清晰又方便地写出sql语句并执行。
FastSQL完全可用于生产环境,相比Mybatis,使用简单,开发效率高。
# 2 入门
## 2.1 安装
如果使用 Maven 来构建项目,则需将下面的 dependency 代码置于 pom.xml 文件中:
```xml
top.fastsql
fastsql
1.3.0
```
如果使用 Gradle 来构建项目,则需将下面的代码置于 build.gradle 文件的 dependencies 代码块中:
```groovy
compile 'top.fastsql:fastsql:1.3.0'
```
## 2.2 构建 SQLFactory
你可以直接从 Java 程序构建一个 SQLFactory ,如果使用SQL的执行功能,至少需要设置 DataSource 。
```java
//新建一个DataSource(这里使用了spring-jdbc的SimpleDriverDataSource)
//也可以使用支持连接池的各种DataSource,如DruidDataSource等
DataSource dataSource = new SimpleDriverDataSource(<...省略参数...>);
SQLFactory sqlFactory = new SQLFactory();
sqlFactory.setDataSource(dataSource);
```
## 2.3 从 SQLFactory 中获取 SQL
既然有了 SQLFactory ,我们就可以从中获得 SQL 的实例了。SQL类完全包含了面向数据库执行 sql 命令所需的所有方法。
你可以通过 SQL 实例来构建并直接执行 SQL 语句。例如:
```java
SQL sql = sqlFactory.sql();
Student student = sql.SELECT("*").FROM("student").WHERE("id=101").queryOne(Student.class);
```
## 2.4 作用域(Scope)和生命周期
### SQLFactory
SQLFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。
使用 SQLFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SQLFactory 被视为一种代码“坏味道(bad smell)”。
因此 SQLFactory 的最佳作用域是应用的作用域。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式
(如果在Spring环境中,利用Spring容器的功能,你完全可以把它设置为一个单例bean)。
### SQL
SQL 实例是有状态的 ,不是线程安全的,是不能被共享的。即使在同一个线程中每执行sql语句一次,都需要重新构建一个 SQL 实例。
绝对不能将 SQL 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。
# 3 SQLFactory 配置
新建SQLFactory
```java
SQLFactory sqlFactory = new SQLFactory();
```
指定DataSource
```java
//新建任意类型一个DataSource,如SimpleDriverDataSource(Spring内部提供的)
// 或者其他支持连接池的DataSource
DataSource dataSource = ... ;
//设置数据源
sqlFactory.setDataSource(dataSource);
```
设置数据源类型
```java
sqlFactory.setDataSourceType(DataSourceType.POSTGRESQL);//默认
//支持 DataSourceType.POSTGRESQL、 DataSourceType.MY_SQL、DataSourceType.ORACLE
```
其他设置
```java
sqlFactory.setQueryTimeout(5000);//设置最大超时时间
sqlFactory.setMaxRows(100);//查询最大行数
```
# 4 使用SQL类构建sql语句
Java程序员面对的最痛苦的事情之一就是在Java代码中嵌入SQL语句。`SQL`类可以简化你构建sql语句的过程。
## 4.1 基本查询
SELECT方法可以传入一个可变参数,以便选择多列。(FastSQL中建议SQL关键字全部采用大写)
```java
sqlFactory.sql().SELECT("name", "age").FROM("student").WHERE("age>10").build();
//==> SELECT name,age FROM student WHERE age>10
sqlFactory.sql().SELECT("name", "age").FROM("student").WHERE("name='小红'").build();
//==> SELECT name,age FROM student WHERE name='小红'
```
`WHERE()`关键字生成`WHERE 1=1`语句,动态sql构建如下
```java
SQL sql = sqlFactory.sql().SELECT("name", "age").FROM("student").WHERE();
if (true){
sql.AND("age > 10");
}
if (false){
sql.AND("age < 8");
}
//生成sql=>SELECT name,age FROM student WHERE 1 = 1 AND age > 10
```
## 4.2 使用操作符方法
FastSQL提供了一些操作符方便SQL的构建,比如:
```java
sqlFactory.sql()
.SELECT("name", "age")
.FROM("student")
.WHERE("age").lt("10")
.AND("name").eq("'小明'")
.build();
//生成sql=> SELECT name,age FROM student WHERE age < 10 AND name = '小明'
```
如下:
| 方法 | 说明 |
| :--------------- | :---------------------------------------------------- |
| eq(String) | 生成 = ,并追加参数(equal的缩写) |
| gt(String) | 生成 > ,并追加参数(是greater than的缩写) |
| gtEq(String) | 生成 >= ,并追加参数(是greater than or equal的缩写) |
| lt(String) | 生成 < ,并追加参数(是less than的缩写 ) |
| ltEq(String) | 生成 <= ,并追加参数(是less than or equal的缩写) |
| nEq(String) | 生成 != ,并追加参数(是not equal的缩写 ) |
| LIKE(String) | 生成 LIKE ,并追加参数, |
| NOT_LIKE(String) | 生成 NOT LIKE ,并追加参数 |
| IS_NULL() | 生成 IS NULL |
| IS_NOT_NULL() | 生成 IS NOT NULL |
| eq() | 生成 = |
| gt() | 生成 > |
| gtEq() | 生成 >= |
| lt() | 生成 < |
| ltEq() | 生成 <= |
| nEq() | 生成 != |
| LIKE() | 生成 LIKE |
| NOT_LIKE() | 生成 NOT LIKE |
### byType(Object)
这些方法仅仅是字符串连接:`eq("1")`生成` = 1` ,`eq("'1'")`会生成` = '1'`。byType(Object)方法可以根据类型生成你想要的sql字符串
```java
sqlFactory.sql()
.SELECT("name", "age")
.FROM("student")
.WHERE("age").lt().byType(10)
.AND("name").eq().byType("小明")
.build();
//==>SELECT name,age FROM student WHERE age < 10 AND name = '小明'
```
| 方法 | 说明 |
| :--------------- | :-------------------------------------------------------------- |
| byType(Object) | 根据类型生成相应字符串 ,如 byType(1)生成1 ,byType("1")生成'1' |
| eqByType(Object) | 使用 = 连接根据类型生成相应的字符串 |
## 4.3 使用连接查询/排序
查询不及格的成绩
```java
sqlFactory.sql().SELECT("s.name","c.subject_name","c.score_value")
.FROM("score c")
.LEFT_JOIN_ON("student s", "s.id=c.student_id")
.WHERE("c.score_value<60")
.ORDER_BY("c.score_value")
.build();
/*
生成sql =>
SELECT s.name, c.subject,c.score_value
FROM score c
LEFT OUTER JOIN student s ON (s.id = c.student_id)
WHERE c.score_value < 60
ORDER BY c.score_value
*/
```
## 4.4 分组查询
查询每个学生总分数
```java
sqlFactory.sql().SELECT("s.name", "sum(c.score_value) total_score")
.FROM("score c")
.LEFT_JOIN_ON("student s", "s.id=c.student_id")
.GROUP_BY("s.name")
.build()
/*
生成sql==>
SELECT s.name, sum(c.score_value) total_score
FROM score c
LEFT OUTER JOIN student s ON (s.id = c.student_id)
GROUP BY s.name
*/
```
## 4.5 IN语句
由于Jdbc规范不支持IN参数绑定,FastSQL提供了几种IN语句直接拼接的方式:
```java
//1.使用字符串
sqlFactory.sql().SELECT("*")
.FROM("student")
.WHERE("name").IN("('小明','小红')")
.build();
//2.使用集合(List,Set等)
sqlFactory.sql().SELECT("*")
.FROM("student")
.WHERE("name").IN(Lists.newArrayList("小明","小红"))
.build();
//3.使用数组
sqlFactory.sql().SELECT("*")
.FROM("student")
.WHERE("name").IN(new Object[]{"小明","小红"})
.build();
//4.使用可变参数(最简洁)
sqlFactory.sql().SELECT("*")
.FROM("student")
.WHERE("name").IN_var("小明","小红")
.build();
//生成sql==> SELECT * FROM student WHERE name IN ('小明','小红')
```
## ~~4.6 使用$_$()方法进行子查询~~
~~查询大于平均分的成绩(可以使用 $_$()方法)~~
```java
sqlFactory.sql().SELECT("*")
.FROM("score")
.WHERE("score_value >")
.$_$(
sqlFactory.sql().SELECT("avg(score_value)").FROM("score")
)
.build();
//生成sql==>
//SELECT * FROM score
//WHERE score_value > ( SELECT avg(score_value) FROM score )
```
~~带有IN的子查询~~
```java
sqlFactory.sql().SELECT("*")
.FROM("score")
.WHERE()
.AND("score")
.IN().$_$(
sqlFactory.sql().SELECT("DISTINCT score_value").FROM("score")
)
.build();
//生成sql==> SELECT * FROM score WHERE 1 = 1 AND score IN (SELECT DISTINCT score_value FROM score)
```
注:复杂sql推荐使用字符串直接构建:
```java
String sql="SELECT * FROM score WHERE 1 = 1 AND score IN (SELECT DISTINCT score_value FROM score)";
sqlFactory.sql().str(sql);
```
## 4.7 AND和OR结合使用
如果查询年龄大于10岁,并且名字是小明或小红
```java
sqlFactory.sql().SELECT("*")
.FROM("student")
.WHERE("age>10")
.AND("(name='小明' OR name='小红')")//手动添加括号
.build();
//或者
sqlFactory.sql().SELECT("*")
.FROM("student")
.WHERE("age>10")
.AND().$_$("name='小明' OR name='小红'")//$_$ 生成左右括号
.build();
```
## 4.8 使用Lambda表达式简化构建动态sql
- `ifTrue(boolean bool, Consumer sqlConsumer)`:如果第1个参数为true,则执行第二个参数(Lambda表达式)
- `ifNotEmpty(Collection> collection, Consumer sqlConsumer)`:如果第1个参数长度大于0,则执行第二个参数(Lambda表达式)
- `ifPresent(Object object, Consumer sqlConsumer)`:如果第1个参数存在(不等于null且不为""),则执行第二个参数(Lambda表达式)
- `ifEquals(Object object1, Object object2, Consumer sQLConsumer)`:如果前两个参数相等,则执行lambda表达式
```java
sqlFactory.sql()
.SELECT("student")
.WHERE("id=:id")
.ifTrue(true, sql -> thisBuilder.AND("name=:name"))
.ifNotEmpty(names, sql -> {
System.out.println("ifNotEmpty?");
thisBuilder.AND("name").IN(Lists.newArrayList("小明", "小红"));
})
.ifPresent("",sql -> {
System.out.println("ifPresent?");
//...处理其他流程语句...
})
.build();
```
输出:
```
ifNotEmpty?
SELECT student WHERE id=:id AND name=:name AND name IN ('小明','小红')
```
## 4.9 分页功能
### 使用原生关键字进行分页
```java
sqlFactory.sql().SELECT("*").FROM("student").LIMIT(10).build();
sqlFactory.sql().SELECT("*").FROM("student").LIMIT(5, 10).build(); //mysql中的写法
sqlFactory.sql().SELECT("*").FROM("student").LIMIT(10).OFFSET(5).build(); //postgresql中的写法
```
生成如下SQL
```sql
SELECT * FROM student LIMIT 10;
SELECT * FROM student LIMIT 5,10;
SELECT * FROM student LIMIT 10 OFFSET 5;
```
### 使用 `pageThis(int,int)` 分页方法进行分页
```
//
sqlFactory.setDataSourceType(DataSourceType.POSTGRESQL); //使用枚举指定数据源类型
sqlFactory.sql().SELECT("*").FROM("student").pageThis(1,10).build();
```
注意:如果不指定 dataSourceType,将会默认使用 postgresql 数据库类型进行分页;
### 使用 `countThis()` 生成获取数量语句
```java
//countThis
sqlFactory.sql().SELECT("*").FROM("student").countThis().buildAndPrintSQL();
```
## 4.10 构建插入insert/修改update/删除delete语句
### 插入
```java
//使用列
sqlFactory.sql().INSERT_INTO("student", "id", "name", "age")
.VALUES("21", "'Lily'", "12").build();
//=>INSERT INTO student (id,name,age) VALUES (21,'Lily',12)
//不使用列
sqlFactory.sql().INSERT_INTO("student").VALUES("21", "'Lily'", "12").build();
//=>INSERT INTO student VALUES (21,'Lily',12)
```
### 修改
SET(String...items) :SET关键字
```java
sqlFactory.sql().UPDATE("student").SET("name = 'Jack'","age = 9").WHERE("name = 'Mike'").build();
//=> UPDATE student SET name = 'Jack',age = 9 WHERE name = 'Mike'
```
### 构建删除语句
```java
sqlFactory.sql().DELETE_FROM("student").WHERE("id=12").build();
//=>DELETE FROM student WHERE id=12
```
# 5 使用SQL类执行sql语句
## 5.1 创建SqlFactory
```java
//创建任意DataSource对象(这里使用了spring自带的数据源SimpleDriverDataSource)
DataSource dataSource = new SimpleDriverDataSource(
new Driver(), "jdbc:postgresql://192.168.0.226:5432/picasso_dev2?stringtype=unspecified",
"developer", "password");
//创建SqlFactory
SqlFactory sqlFactory = new SqlFactory();
sqlFactory.setDataSource(dataSource);
sqlFactory.setDataSourceType(DataSourceType.MY_SQL);
```
## 5.2 设置参数的方法
FastSQL支持多种传入命名参数的方法:
- `parameter(SqlParameterSource)` 支持传入SqlParameterSource类型的参数(为了兼容spring-jdbc)。
- `beanParameter(Object)`方法可以传入对象参数。
- `mapParameter(Map)`支持传入Map类型参数。
- `mapItemsParameter(Object...)`支持多个key-value形式的参数,比如`mapItemsParameter("id", 12345,"name","小明")`。
- `beanAndMapParameter(Object, Map)` 支持两种不同的参数组合,后一个map会覆盖前面bean中的相同名字的参数。
- `addMapParameterItem(String, Object)`可以为以上几种传参方法追加参数。
>注意:以上方法除了`addMapParameterItem`都**只能调用一次,调用多次会导致前面的传参被覆盖**。如需追加参数,请使用`addMapParameterItem`。
FastSQL也支持?占位符和可变参数:
- `varParameter(Object... vars)` 传入可变参数,**可以调用多次**,用来追加参数。
### 示例
使用beanParameter方法支持传入一个参数bean
```java
public class StudentDTO{
private String name;
private int age;
//省略set和get方法
}
```
```java
StudentDTO dto =new StudentDTO();
dto.setName="小明";
dto.setAge=10;
sqlFactory.sql().SELECT("*")
.FROM("student")
.WHERE("name=:name")
.AND("age>:age")
.beanParameter(dto) //设置一个DTO查询参数
.queryList(StudVO.class);
```
使用beanParameter方法并追加参数
```java
Map param = new HashMap<>();
map.put("name","李%");
sqlFactory.sql()
.SELECT("*")
.FROM("student")
.WHERE("name").LIKE(":name")
.AND("age > :age")
.beanParameter(param) //设置一个map参数
.addParameterMapItem("age",12) //追加
.queryList(Student.class);
```
使用varParameter方法--支持?占位符和可变参数
```
SQL sql = sqlFactory.sql();
sql.INSERT_INTO("student", "id", "name", "age")
.VALUES("?", "?", "?")
.varParameter("123", "小明")
.varParameter(12)
.update();
```
## 5.3 查询方法
### 5.3.1 查询方法解析
- `T queryOne(Class returnClassType)`查询单行结果封装为一个对象,参数可以是可以为String/Integer/Long/Short/BigDecimal/BigInteger/Float/Double/Boolean或者任意POJO的class。
- `Map queryMap()`查询单行结果封装为Map
- `List queryList(Class returnClassType)`查询多行结果封装为一个对象列表
- `List