An open API service indexing awesome lists of open source software.

https://github.com/llllllxy/tiny-jdbc

🌟 tiny-jdbc:一款专为 Java 开发者量身打造的极致轻量级数据库 ORM 神器✨!它深度依托 Spring JdbcTemplate 的强大内核,以精妙的架构设计和卓越的性能表现,无缝融合了 SQL 操作的灵活性与实体类映射操作的便捷性。无论是简单的数据查询,还是复杂的事务处理,tiny-jdbc 都能让数据库操作变得如同行云流水般简单,助你在开发之路上一路狂飙🚀!
https://github.com/llllllxy/tiny-jdbc

database java jdbc-template orm springboot

Last synced: 11 months ago
JSON representation

🌟 tiny-jdbc:一款专为 Java 开发者量身打造的极致轻量级数据库 ORM 神器✨!它深度依托 Spring JdbcTemplate 的强大内核,以精妙的架构设计和卓越的性能表现,无缝融合了 SQL 操作的灵活性与实体类映射操作的便捷性。无论是简单的数据查询,还是复杂的事务处理,tiny-jdbc 都能让数据库操作变得如同行云流水般简单,助你在开发之路上一路狂飙🚀!

Awesome Lists containing this project

README

          

TinyJDBC


一个优雅的 ORM 框架,轻量、灵活、高性能
























star



## 1、简介

`tiny-jdbc` 是一款基于 `Spring JDBC` 精心打造的轻量、灵活且高性能的数据库 `ORM` 框架。它在继承 `Spring JDBC`
原有强大功能的基础上,进行了大量的增强和优化,为开发者提供了更加便捷、高效的数据库操作体验,让操作数据库这件事变得更加简单!

### 优势

- **轻量级**:除了 Spring JDBC 本身,再无任何第三方依赖,轻量可靠
- **性能高**:依托高性能的 Spring JDBC,在执行数据库操作时性能基本无损耗,能够满足高并发场景下的应用需求
- **功能强**:既支持原生 sql 操作,让开发者可以灵活编写复杂的 sql 语句;又支持实体类映射操作,通过简单的配置即可实现对象与数据库表的映射。BaseDao
里封装了大量的通用方法,配合强大灵活的条件构造器(Criteria)和SQL构造器(SQL),基本能满足各类使用需求。
- **支持 Lambda 形式调用**:条件构造器(Criteria)和SQL构造器(SQL)支持Lambda形式调用,编译期语法增强,无需再担心字段写错,提高代码的安全性和可维护性
- **支持主键自动生成**:内含多种主键生成策略,包括自增主键、UUID、雪花ID 等,同时也支持自定义 ID 生成策略,满足不同业务场景下的主键生成需求。
- **支持多种数据库分页方言**:包括 MySQL、ORACLE、DB2、PostgreSql 等多种常用数据库,无需额外配置,框架会自动识别数据库类型并采用相应的分页方言,大大简化了分页查询的开发工作。
- **同时支持 SpringBoot2 和 SpringBoot3 **

### 支持数据库

- 分页插件目前支持MySQL、MARIADB、ORACLE、DB2、PostgreSql、SQLite、H2、OceanBase、openGauss、informix、达梦数据库、人大金仓数据库、瀚高数据库等常见数据库
- 其他操作支持任何使用标准 SQL 的数据库

## 2、快速开始

### 引入Maven依赖

```xml

org.springframework.boot
spring-boot-starter-jdbc

top.lxyccc
tiny-jdbc-boot-starter
1.8.8

```

### application.yml参数配置

- **或application.properties**

```yaml
tiny-jdbc:
# 打印banner,默认false
banner: true
# 分页器适配类型,可不配置,不配置的话会自动获取
db-type: mysql
```

### 定义Entity实体类

- **实体类对应数据库的一张表**
- **实体类属性名必须使用`驼峰命名规则`,与数据库表字段一一映射**

```java

@Table("b_upload_file")
public class UploadFile implements Serializable {
private static final long serialVersionUID = -1L;

/**
* @Id 注解用于标记表的主键,目前只支持单主键,请不要在多个属性上设置此注解,会导致程序出错
*
* idType: 主键ID策略,目前支持以下7种 AUTO_INCREMENT、INPUT、OBJECT_ID、ASSIGN_ID、UUID、SEQUENCE、CUSTOM
* value: 则代表的是sequence 序列的 sql 内容,idType=SEQUENCE时,必须设置此内容
*

* 注意!
* 如果设置为 AUTO_INCREMENT 自增主键 的话,则此字段必须为Long
* 如果设置为 UUID 的话,则此属性类型必须为String
* 如果设置为 OBJECT_ID 的话,则此属性类型必须为String
* 如果设置为 ASSIGN_ID 的话,则此属性类型必须为String或者Long
* 如果设置为 SEQUENCE 的话,则此属性类型必须为Integer或者Long,且必须设置value属性为查询序列值的SQL
* 如果设置为 CUSTOM 的话,则需要自己实现 IdGeneratorInterface 接口并注册为bean
*
*
* @Column 注解用于标记属性和表字段的对应关系
*/
@Id(idType = IdType.AUTO_INCREMENT)
@Column(value = "id")
private Long id;

/**
* 文件id
*/
@Column("file_id")
private String fileId;

/**
* 文件原名称
*/
@Column("file_name_old")
private String fileNameOld;

/**
* 文件新名称
*/
@Column("file_name_new")
private String fileNameNew;

/**
* 文件路径
*/
@Column("file_path")
private String filePath;

/**
* 文件md5
*/
@Column("file_md5")
private String fileMd5;

/**
* 上传时间
*/
@Column("created_at")
private Date createdAt;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

// 其他 getter 和 setter 方法...
}
```

### 定义Dao类,继承自BaseDao

- **泛型一为对应实体类,泛型二实体类主键类型**

```java
import org.springframework.stereotype.Repository;
import org.tinycloud.jdbc.BaseDao;
import org.tinycloud.entity.UploadFile;

@Repository
public class UploadFileDao extends BaseDao {

// 可添加自定义方法...
}
```

### Service层注入使用

```java
import org.springframework.stereotype.Service;
import org.tinycloud.dao.UploadFileDao;

@Service
public class UploadFileService {

@Autowired
private UploadFileDao uploadFileDao;
}
```

## 3、注解说明

#### @Table

- 描述:表名注解,标识实体类对应的表
- 使用位置:实体类

```java

@Table("b_upload_file")
public class UploadFile implements Serializable {
private static final long serialVersionUID = -1L;

}
```

| 属性 | 类型 | 必须指定 | 默认值 | 描述 |
|-------|--------|------|-----|---------|
| value | String | 是 | "" | 对应数据库表名 |

#### @Id

- 描述:主键注解,标识实体类主键属性
- 使用位置:实体类属性

```java

@Table("b_upload_file")
public class UploadFile implements Serializable {
private static final long serialVersionUID = -1L;

@Id(idType = IdType.AUTO_INCREMENT)
@Column(value = "id")
private Long id;
}
```

| 属性 | 类型 | 必须指定 | 默认值 | 描述 |
|--------|--------|------|--------------|------------------------------------------------------------|
| idType | String | 是 | IdType.INPUT | 主键策略 |
| value | String | 否 | "" | 若 idType 类型是 sequence, value 则代表的是sequence 序列的 sql 内容,此时必填 |

##### IdType主键策略说明

| 值 | 描述 |
|----------------|---------------------------------------------------------------|
| INPUT | 自行 set 主键值 |
| AUTO_INCREMENT | 数据库 ID 自增 |
| OBJECT_ID | 自动设置 MongoDb objectId 作为主键值 |
| ASSIGN_ID | 自动设置 雪花ID 作为主键值 |
| UUID | 自动设置 UUID 作为主键值 |
| SEQUENCE | 自动设置 调用序列SQL结果 作为主键值 |
| CUSTOM | 自定义主键ID生成器,需自行实现 IdGeneratorInterface 接口,详见[自定义ID生成器](#idGen) |

#### @Column

- 描述:字段注解,标识实体类属性和字段的对应关系
- 使用位置:实体类属性

```java

@Table("b_upload_file")
public class UploadFile implements Serializable {
private static final long serialVersionUID = -1L;

@Id(idType = IdType.AUTO_INCREMENT)
@Column(value = "id")
private Long id;

@Column("file_id")
private String fileId;
}
```

| 属性 | 类型 | 必须指定 | 默认值 | 描述 |
|-------|--------|------|-----|----------|
| value | String | 是 | "" | 对应数据库字段名 |

## 4、BaseDao CRUD接口说明

### 查询操作

| 方法 | 说明 |
|---------------------------------------------------------------------------------|----------------------------------------------------------|
| `List selectAll();` | 查询所有数据并返回实体类对象列表,类型使用的是xxxDao的类型 |
| ` List select(String sql, Class classz, Object... params);` | 根据给定的sql语句和实体类型和参数,查询数据并返回实体类对象列表 |
| `List select(String sql, Object... params);` | 根据给定的sq语句l和参数,查询数据并返回实体类对象列表,类型使用的是xxxDao的类型 |
| ` F selectOne(String sql, Class classz, Object... params);` | 根据给定的sql语句和实体类型和参数,查询数据并返回一个实体类对象 |
| `T selectOne(String sql, Object... params);` | 根据给定的sql语句和参数,查询数据并返回一个实体类对象,类型使用的是xxxDao的类型 |
| `List> selectMap(String sql, Object... params);` | 根据给定的sql语句和参数,查询数据并返回Map列表 |
| `Map selectOneMap(String sql, Object... params);` | 根据给定的sql语句和参数,查询数据并返回一个Map对象 |
| ` T selectForObject(String sql, Class clazz, Object... params);` | 根据给定的sql语句和实体类型和参数,查询数据并返回一个值(常用于查count) |
| `Page paginate(String sql, Class clazz, Page page, Object... params);` | 根据给定的sql语句和参数,执行分页查询,返回Page对象,类型使用的Class传入的自定义类型 |
| `Page paginate(String sql, Page page, Object... params);` | 根据给定的sql语句和参数,执行分页查询,返回Page对象,类型使用的是xxxDao的类型 |
| `T selectById(ID id);` | 根据主键ID值,查询数据并返回一个实体类对象,类型使用的是xxxDao的类型 |
| `T selectByIds(List ids);` | 根据主键ID值列表,查询数据并返回实体类对象列表,类型使用的是xxxDao的类型 |
| `T selectByIds(ID... id);` | 根据主键ID值可变参数列表,查询数据并返回实体类对象列表,类型使用的是xxxDao的类型 |
| `List select(T entity);` | 实体类里面非null的属性作为查询条件,查询数据库并返回实体类对象列表,类型使用的是xxxDao的类型 |
| `List select(QueryCriteria criteria);` | 根据查询构造器查询,返回多条,类型使用的是xxxDao的类型 |
| `List select(LambdaQueryCriteria lambdaCriteria);` | 根据查询构造器(lambda)查询,返回多条,查询数据并返回一个实体类对象,类型使用的是xxxDao的类型 |
| `T selectOne(T entity);` | 实体类里面非null的属性作为查询条件,查询数据并返回一个实体类对象,类型使用的是xxxDao的类型 |
| `T selectOne(QueryCriteria criteria);` | 根据查询构造器执行查询,返回一条,类型使用的是xxxDao的类型 |
| `T selectOne(LambdaQueryCriteria lambdaCriteria);` | 根据查询构造器(lambda)执行查询,返回一条,类型使用的是xxxDao的类型 |
| `Page paginate(T entity, Page page);` | 根据实体类里面非null的属性作为查询条件,执行分页查询,返回Page对象,类型使用的是xxxDao的类型 |
| `Page paginate(QueryCriteria criteria, Page page);` | 根据查询构造器执行分页查询,返回Page对象,类型使用的是xxxDao的类型 |
| `Page paginate(LambdaQueryCriteria lambdaCriteria, Page page);` | 根据查询构造器(lambda)执行分页查询,返回Page对象,类型使用的是xxxDao的类型 |
| `Long selectCount(QueryCriteria criteria);` | 根据查询构造器执行总记录数查询,返回符合条件的总记录数量 |
| `Long selectCount(LambdaQueryCriteria lambdaCriteria);` | 根据查询构造器(lambda)执行总记录数查询,返回符合条件的总记录数量 |
| `boolean exists(QueryCriteria criteria);` | 根据查询构造器执行查询记录是否存在,返回true或者false |
| `boolean exists(LambdaQueryCriteria lambdaCriteria);` | 根据查询构造器(lambda)执行查询记录是否存在,返回true或者false |

### 插入操作

| 方法 | 说明 |
|------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
| `int insert(String sql, Object... params);` | 根据提供的sql语句和提供的参数,执行插入 |
| `int insert(T entity);` | 插入entity里的数据,忽略entity里值为null的属性,如果主键策略为assignId、uuid、objectId或custom,那将在entity里返回自动生成的主键值 |
| `int insert(T entity, boolean ignoreNulls);` | 插入entity里的数据,可选择是否忽略entity里值为null的属性,如果主键策略为assignId、uuid、objectId或custom,那将在entity里返回自动生成的主键值 |
| `Long insertReturnAutoIncrement(T entity);` | 插入entity里的数据,将忽略entity里属性值为null的属性,并且返回自增的主键 |
| `Long insertReturnAutoIncrement(T entity, boolean ignoreNulls);` | 插入entity里的数据,可选择是否忽略entity里值为null的属性,并且返回自增的主键 |

### 更新操作

| 方法 | 说明 |
|--------------------------------------------------------------------------------|---------------------------------------------------------------------|
| `int update(String sql, Object... params);` | 根据提供的SQL语句和提供的参数,执行修改 |
| `int updateById(T entity);` | 根据entity提供SET子句,主键值提供WHERE条件,执行修改,默认忽略entity里值为null的属性 |
| `int updateById(T entity, boolean ignoreNulls);` | 根据entity提供SET子句,主键值提供WHERE条件,执行修改,可选择是否忽略entity里值为null的属性 |
| `int update(T entity, UpdateCriteria criteria);` | 根据entity提供SET子句,条件构造器提供WHERE条件,执行修改,默认忽略entity里值为null的属性 |
| `int update(T entity, LambdaUpdateCriteria criteria);` | 根据entity提供SET子句,条件构造器(lambda)提供WHERE条件,执行修改,默认忽略entity里值为null的属性 |
| `int update(T entity, boolean ignoreNulls, UpdateCriteria criteria);` | 根据entity提供SET子句,条件构造器,执行修改提供WHERE条件,执行修改,可选择是否忽略entity里值为null的属性 |
| `int update(T entity, boolean ignoreNulls, LambdaUpdateCriteria criteria);` | 根据entity提供SET子句,条件构造器(lambda)提供WHERE条件,执行修改,可选择是否忽略entity里值为null的属性 |
| `int update(UpdateCriteria criteria);` | 只根据条件构造器来构建,需配合.set方法来使用 |
| `int update(LambdaUpdateCriteria criteria);` | 只根据条件构造器(lambda)来构建,需配合.set方法来使用 |

### 删除操作

| 方法 | 说明 |
|-------------------------------------------------|-----------------------------------------------|
| `int delete(String sql, Object... params);` | 根据提供的SQL语句和提供的参数,执行删除 |
| `int deleteById(ID id);` | 根据主键ID进行删除,类型使用的是xxxDao的类型 |
| `int deleteByIds(List ids);` | 根据主键ID列表进行删除,类型使用的是xxxDao的类型 |
| `int deleteByIds(ID... ids);` | 根据主键ID可变参数列表进行删除,类型使用的是xxxDao的类型 |
| `int delete(T entity);` | 根据entity里的属性值进行删除,entity里不为null的属性,将作为where参数 |
| `int delete(UpdateCriteria criteria);` | 根据条件构造器,将作为where参数 |
| `int delete(LambdaUpdateCriteria criteria);` | 根据条件构造器(lambda),将作为where参数 |

### 使用SQL构造器进行操作(`1.8.2`版本新增)

| 方法 | 说明 |
|------------------------------------------------------------|---------------------------------------------------|
| `int execute(SQL sql);` | 根据提供的SQL构造器,执行删除、新增、或更新操作 |
| `int delete(SQL sql);` | 根据提供的SQL构造器,执行删除操作 |
| `int update(SQL sql);` | 根据提供的SQL构造器,执行更新操作 |
| `int insert(SQL sql);` | 根据提供的SQL构造器,执行新增操作 |
| ` List select(String SQL, Class classz);` | 根据给定的SQL构造器和实体类型数,查询数据库并返回实体类对象列表 |
| `List select(SQL sql)` | 根据给定的SQL构造器,查询数据库并返回实体类对象列表,类型使用的是xxxDao的类型 |
| `Page paginate(SQL sql, Page page);` | 根据给定的SQL构造器,执行分页查询,返回Page对象,类型使用的是xxxDao的类型 |
| `Page paginate(SQL sql, Class clazz, Page page);` | 根据给定的SQL构造器,执行分页查询,返回Page对象,类型使用的Class传入的自定义类型 |
| `T selectOne(SQL sql);` | 根据给定的SQL构造器,查询数据并返回一个实体类对象,类型使用的是xxxDao的类型 |
| ` F selectOne(SQL sql, Class clazz);` | 根据给定的SQL构造器,查询数据并返回一个实体类对象,类型使用的Class传入的自定义类型 |

## 5、条件构造器(Criteria)

### 功能使用说明

#### 条件构造器(AbstractCriteria & AbstractLambdaCriteria)

> QueryCriteria(LambdaQueryCriteria) 和 UpdateCriteria(LambdaUpdateCriteria) 的父类
> 用于生成 sql 的 where 条件
>
> 其中带Lambda字样的构造器 是基于 Lambda 表达式的条件构造器,它允许你使用 Lambda 表达式来指定字段,避免了硬编码字段名的问题。
>
以下方法均有重载whether参数(第一个参数)后的方法,当whether=false时,不执行后面的条件,例如:`eq(boolean whether, String field, R value)`

| 方法 | 说明 | 普通示例 | lambda示例 | 等价SQL |
|----------------|-----------------------|--------------------------------------------|----------------------------------------------------------|----------------------------------|
| eq | 等于 = | eq("name", "张三") | eq(User::getName, "张三") | AND name = '张三' |
| orEq | 等于 = | orEq("name", "张三") | orEq(User::getName, "张三") | OR name = '张三' |
| notEq | 不等于 <> | notEq("name", "张三") | notEq(User::getName, "张三") | AND name <> '张三' |
| orNotEq | 不等于 <> | orNotEq("name", "张三") | orNotEq(User::getName, "张三") | OR name <> '张三' |
| isNull | 等于null | isNull("name") | isNull(User::getName) | AND name IS NULL |
| orIsNull | 等于null | orIsNull("name") | orIsNull(User::getName) | OR name IS NULL |
| isNotNull | 不等于null | isNotNull("name") | isNotNull(User::getName) | AND name IS NOT NULL |
| orIsNotNull | 不等于null | orIsNotNull("name") | orIsNotNull(User::getName) | OR name IS NOT NULL |
| lt | 小于 < | lt("name", "张三") | lt(User::getName, "张三") | AND name < '张三' |
| orLt | 小于 < | orLt("name", "张三") | orLt(User::getName, "张三") | OR name < '张三' |
| lte | 小于等于 <= | lte("name", "张三") | lte(User::getName, "张三") | AND name <= '张三' |
| orLte | 小于等于 <= | orLte("name", "张三") | orLte(User::getName, "张三") | OR name <= '张三' |
| gt | 大于 > | gt("name", "张三") | gt(User::getName, "张三") | AND name > '张三' |
| orGt | 大于 > | orGt("name", "张三") | orGt(User::getName, "张三") | OR name > '张三' |
| gte | 大于等于 >= | gte("name", "张三") | gte(User::getName, "张三") | AND name >= '张三' |
| orGte | 大于等于 >= | orGte("name", "张三") | orGte(User::getName, "张三") | OR name >= '张三' |
| in | SQL里的IN操作 | in("name", {"张三","李四"}) | in(User::getName, "张三") | AND name IN ('张三','李四') |
| orIn | SQL里的IN操作 | orIn("name", {"张三","李四"}) | orIn(User::getName, "张三") | OR name IN ('张三','李四') |
| notIn | SQL里的IN操作 | notIn("name", "张三") | notIn(User::getName, "张三") | AND name NOT IN ('张三','李四') |
| orNotIn | SQL里的IN操作 | orNotIn("name", {"张三","李四"}) | orNotIn(User::getName, "张三") | OR name NOT IN ('张三','李四') |
| between | NOT BETWEEN 值1 AND 值2 | between("age", 10, 18) | between(User::getAge, 10, 18) | AND (age BETWEEN 10 AND 18 ) |
| orBetween | NOT BETWEEN 值1 AND 值2 | orBetween("age", 10, 18) | orBetween(User::getAge, 10, 18) | OR (age BETWEEN 10 AND 18 ) |
| notBetween | NOT BETWEEN 值1 AND 值2 | notBetween("age", 10, 18) | notBetween(User::getAge, 10, 18) | AND (age NOT BETWEEN 10 AND 18 ) |
| orNotBetween | NOT BETWEEN 值1 AND 值2 | orNotBetween("age", 10, 18) | orNotBetween(User::getAge, 10, 18) | OR (age NOT BETWEEN 10 AND 18 ) |
| like | LIKE '%值%' | like("name", "张三") | like(User::getName, "张三") | AND name LIKE '%张三%' |
| orLike | LIKE '%值%' | orLike("name", "张三") | orLike(User::getName, "张三") | OR name LIKE '%张三%' |
| notLike | NOT LIKE '%值%' | notLike("name", "张三") | notLike(User::getName, "张三") | AND name NOT LIKE '%张三%' |
| orNotLike | NOT LIKE '%值%' | orNotLike("name", "张三") | orNotLike(User::getName, "张三") | OR name NOT LIKE '%张三%' |
| like | LIKE '%值%' | like("name", "张三") | like(User::getName, "张三") | AND name LIKE '%张三%' |
| orLike | LIKE '%值%' | orLike("name", "张三") | orLike(User::getName, "张三") | OR name LIKE '%张三%' |
| notLike | NOT LIKE '%值%' | notLike("name", "张三") | notLike(User::getName, "张三") | AND name NOT LIKE '%张三%' |
| orNotLike | NOT LIKE '%值%' | orNotLike("name", "张三") | orNotLike(User::getName, "张三") | OR name NOT LIKE '%张三%' |
| leftLike | LIKE '%值' | leftLike("name", "张三") | leftLike(User::getName, "张三") | AND name LIKE '%张三' |
| orLeftLike | LIKE '%值' | orLeftLike("name", "张三") | orLeftLike(User::getName, "张三") | OR name LIKE '%张三' |
| notLeftLike | NOT LIKE '%值' | notLeftLike("name", "张三") | notLeftLike(User::getName, "张三") | AND name NOT LIKE '%张三' |
| orNotLeftLike | NOT LIKE '%值' | orNotLeftLike("name", "张三") | orNotLeftLike(User::getName, "张三") | OR name NOT LIKE '%张三' |
| rightLike | LIKE '值%' | rightLike("name", "张三") | rightLike(User::getName, "张三") | AND name LIKE '张三%' |
| orRightLike | LIKE '值%' | orRightLike("name", "张三") | orRightLike(User::getName, "张三") | OR name LIKE '张三%' |
| notRightLike | NOT LIKE '值%' | notRightLike("name", "张三") | notRightLike(User::getName, "张三") | AND name NOT LIKE '张三%' |
| orNotRightLike | NOT LIKE '值%' | orNotRightLike("name", "张三") | orNotRightLike(User::getName, "张三") | OR name NOT LIKE '张三%' |
| or | OR 嵌套 | or(i -> i.eq("name", "张三").lt("age", 18)) | or(i -> i.eq(User::getName, "张三").lt(User::getAge, 18)) | OR (name = '张三' AND age < 18) |
| and | AND 嵌套 | and(i -> i.eq("name", "张三").lt("age", 18)) | and(i -> i.eq(User::getName, "张三").lt(User::getAge, 18)) | AND (name = '张三' AND age < 18) |

#### 查询构造器(QueryCriteria & LambdaQueryCriteria)

> 继承自条件构造器,可额外自定义查询的排序和字段内容,查询接口适用

| 方法 | 说明 | 普通示例 | lambda示例 | 等价SQL |
|-------------|------------------------|-----------------------|-------------------------------------|--------------------|
| orderBy | 排序,true=desc,false=asc | orderBy("name", true) | orderBy(User::getName, true) | ORDER BY name DESC |
| orderBy | 升序排序 | orderBy("name") | orderBy(User::getName) | ORDER BY name |
| orderByDesc | 降序排序 | orderByDesc("name") | orderByDesc(User::getName) | ORDER BY name DESC |
| select | 设置查询字段 | select("name", "age") | select(User::getName, User::getAge) | SELECT name,age |
| last | 直接在查询的最后添加一个 SQL 片段 | last("LIMIT 1") | last("LIMIT 1") | LIMIT 1 |

##### QueryCriteria示例

```java
List ids = new ArrayList() {{
add(1);
add(2);
add(3);
}};

QueryCriteria criteria = new QueryCriteria<>();
criteria.lt("age", 28);
criteria.eq("created_at", new Date());
criteria.in("id", ids);
criteria.orderBy("age", true);
// 等价于 SELECT 所有字段 FROM person WHERE age < 28 AND created_at = '2023-08-05 17:31:26' AND id IN (1,2,3) ORDER BY age DESC

QueryCriteria criteria = new QueryCriteria<>();
criteria.select("id", "name");
criteria.lt("age", 28);
// 等价于 SELECT id,name FROM person WHERE age < 28
```

##### LambdaQueryCriteria示例

```java
List ids = new ArrayList() {{
add(1L);
add(2L);
add(3L);
}};

LambdaQueryCriteria criteria = new LambdaQueryCriteria<>();
criteria.lt(UploadFile::getFileId, "1000");
criteria.eq(UploadFile::getFileMd5, "b8394b15e02c50b508b3e46cc120f0f5");
criteria.in(UploadFile::getId, ids);
criteria.orderBy(UploadFile::getCreatedAt, true);
// 等价于 SELECT 所有字段 FROM b_upload_file WHERE file_id < '1000' AND file_md5 = 'b8394b15e02c50b508b3e46cc120f0f5' AND id IN (1,2,3) ORDER BY created_at DESC

LambdaQueryCriteria criteria = new LambdaQueryCriteria<>();
criteria.select(UploadFile::getFileId, UploadFile::getFileMd5);
criteria.lt(UploadFile::getFileId, "1000");
// 等价于 SELECT file_id,file_md5 FROM b_upload_file WHERE file_id < '1000'
```

#### 更新构造器(UpdateCriteria & LambdaUpdateCriteria)

> 继承自条件构造器,可额外自定义更新的字段内容和值,更新接口适用

| 方法 | 说明 | 普通示例 | lambda示例 | 等价SQL |
|--------------|--------|------------------------|-------------------------------|-------------------|
| set | 设置更新字段 | set("name", "张三") | set(User::getName, "张三") | set name = '张三' |
| setIncrement | 设置字段自增 | setIncrement("age", 1) | setIncrement(User::getAge, 1) | set age = age + 1 |
| setDecrement | 设置字段自减 | setDecrement("age", 1) | setDecrement(User::getAge, 1) | set age = age - 1 |

##### UpdateCriteria示例

```java
int num = projectInfoDao.update(new UpdateCriteria()
.set("created_at", new Date())
.set("updated_by", "admin3")
.eq("project_name", "批量项目1"));
// 等价于 UPDATE t_project_info SET created_at = '2023-08-05 17:31:26', updated_by = 'admin3' WHERE project_name = '批量项目1'

TProjectInfo project1 = new TProjectInfo();
project1.setCreatedAt(new Date());
project1.setUpdatedBy("admin3");

int num = projectInfoDao.update(project1, new UpdateCriteria().eq("project_name", "批量项目1"));
// 等价于 UPDATE t_project_info SET created_at = '2023-08-05 17:31:26', updated_by = 'admin3' WHERE project_name = '批量项目1'
```

##### LambdaUpdateCriteria示例

```java
int num = projectInfoDao.update(new LambdaUpdateCriteria()
.set(TProjectInfo::getEnableAt, new Date())
.set(TProjectInfo::getUpdatedBy, "admin2")
.eq(TProjectInfo::getProjectName, "批量项目1"));
// 等价于 UPDATE t_project_info SET enable_at = '2023-08-05 17:31:26', updated_by = 'admin2' WHERE project_name = '批量项目1'

TProjectInfo project1 = new TProjectInfo();
project1.setCreatedAt(new Date());
project1.setUpdatedBy("admin3");

int num = projectInfoDao.update(project1, new LambdaUpdateCriteria().eq(TProjectInfo::getProjectName, "批量项目1"));
// 等价于 UPDATE t_project_info SET created_at = '2023-08-05 17:31:26', updated_by = 'admin3' WHERE project_name = '批量项目1'
```

#### CriteriaBuilder的使用和示例

Tiny-Jdbc 提供了 CriteriaBuilder 类,它是一个静态工厂类,用于快速创建 QueryCriteria、LambdaQueryCriteria、UpdateCriteria 和
LambdaUpdateCriteria 的实例。使用 CriteriaBuilder 可以减少代码量,提高开发效率。

```java
LambdaQueryCriteria queryCriteria = CriteriaBuilder.lambdaQuery()
.select(TProjectInfo::getId, TProjectInfo::getProjectName, TProjectInfo::getCreatedAt)
.eq(TProjectInfo::getProjectName, "xxxx")
.eq(TProjectInfo::getId, 1709630713614L);
```

## 6、SQL构造器(SQL)

Tiny-Jdbc 提供了 SQL 类,它是一个静态工厂类,用于快速创建 SQL 的实例。使用 SQL 可以减少代码量,提高开发效率。
[使用文档](SQL.md)

7、自定义ID生成器

需要实现 IdGeneratorInterface 接口,并且声明为 Bean 供 Spring 扫描注入

### 方式一:使用@Component注册成bean

```java

@Component
public class CustomIdGeneratorInterface implements IdGeneratorInterface {

@Override
public Object nextId(Object entity) {
// 可以将当前传入的class全类名来作为业务键,
// 或者通过反射获取表名进行业务Id调用生成
String bizKey = entity.getClass().getName();
// 自定义ID生成策略,推荐最终返回值使用包装类
Long id = ....;
// 返回生成的id值即可.
return id;
}
}
```

### 方式二:在@Configuration配置类中注册

```java

@Bean
public IdGeneratorInterface idGenerator() {
return new IdGeneratorInterface() {
@Override
public Object nextId(Object entity) {
// 可以将当前传入的class全类名来作为业务键,
// 或者通过反射获取表名进行业务Id调用生成
String bizKey = entity.getClass().getName();
// 自定义ID生成策略,推荐最终返回值使用包装类
Long id = ....;
// 返回生成的id值即可.
return id;
}
};
}
```

## 8、自定义雪花ID算法数据中心标识和机器标识

需要实现 SnowflakeConfigInterface 接口,并且声明为 Bean 供 Spring 扫描注入

### 方式一:使用@Component注册成bean

```java

@Component
public class CustomSnowflakeConfigInterface implements SnowflakeConfigInterface {

@Override
public DatacenterAndWorkerProvider getDatacenterIdAndWorkerId() {
DatacenterAndWorkerProvider provider = new DatacenterAndWorkerProvider();

// 自定义datacenterId和workerId生成逻辑
Long datacenterId = ....;
Long workerId = ....;

provider.setDatacenterId(datacenterId);
provider.setWorkerId(workerId);
return provider;
}
}
```

### 方式二:在@Configuration配置类中注册

```java

@Bean
public SnowflakeConfigInterface snowflakeConfigInterface() {
return new SnowflakeConfigInterface() {
@Override
public DatacenterAndWorkerProvider getDatacenterIdAndWorkerId() {
DatacenterAndWorkerProvider provider = new DatacenterAndWorkerProvider();
// 自定义datacenterId和workerId生成逻辑
Long datacenterId = ....;
Long workerId = ....;
provider.setDatacenterId(datacenterId);
provider.setWorkerId(workerId);
return provider;
}
};
}
```

## 9、无Entity实体类操作

Tiny-Jdbc 提供了 `JdbcTemplateHelper` 工具类,支持无实体类映射场景下的灵活数据操作。适用于:
- 无明确业务实体的统计分析场景(如数据大屏、聚合报表)
- 多表联合查询且无需实体映射的场景
- 临时性或灵活性较高的数据操作需求

### 核心优势
- **无需继承 BaseDao**:直接通过工具类调用,解耦实体依赖
- **支持复杂 SQL**:可执行任意自定义 SQL 语句
- **全操作覆盖**:包含查询、分页、插入、更新、删除等完整能力

#### 使用示例

通过 Spring 依赖注入使用,示例如下:

```java
@Repository
public class OtherDao {

@Autowired
private JdbcTemplateHelper helper;

// 多表联合查询示例
public List queryDashboardData() {
String sql = "SELECT " +
"p.id AS projectId, " +
"p.name AS projectName, " +
"COUNT(t.id) AS taskCount, " +
"SUM(CASE WHEN t.status = 'COMPLETED' THEN 1 ELSE 0 END) AS completedCount " +
"FROM t_project p " +
"LEFT JOIN t_task t ON p.id = t.project_id " +
"GROUP BY p.id";

return helper.select(sql, DashboardVO.class);
}

// 分页查询示例
public Page paginateProjects(int pageNum, int pageSize) {
Page page = new Page<>(pageNum, pageSize);
String sql = "SELECT id, name, create_time FROM t_project ORDER BY create_time DESC";
return helper.paginate(sql, ProjectInfo.class, page);
}

// 插入操作示例
public int createProject(Map projectData) {
String sql = "INSERT INTO t_project (name, owner_id, status) VALUES (?, ?, ?)";
return helper.insert(
sql,
projectData.get("name"),
projectData.get("ownerId"),
projectData.get("status")
);
}
}
```

## 10、一些使用示例

创建 ProjectInfoDao

```java

import org.example.entity.TProjectInfo;
import org.springframework.stereotype.Repository;
import org.tinycloud.jdbc.BaseDao;

@Repository
public class ProjectInfoDao extends BaseDao {

}
```

1. 查询操作

```java

@Autowired
private ProjectDao projectDao;

// 查询所有的项目,返回列表
List projectList = projectDao.select("select * from t_project_info order by created_at desc");

// 自定义返回类型,返回列表
List projectList = projectDao.select("select * from t_project_info order by created_at desc", ProjectInfoVo.class);

// 查询所有的项目,返回列表
List projectList = projectDao.select("select * from t_project_info order by created_at desc");

// 查询所以的项目,返回Map列表
List> projectList = projectDao.selectMap("select * from t_project_info order by created_at desc");

// 查询id=1的项目,返回列表
List projectList = projectDao.select("select * from t_project_info where id = ? ", 1);

// 模糊查询项目,返回列表
List projectList = projectDao.select("select * from t_project_info where project_name like CONCAT('%', ?, '%')", "测试项目");

// 查询id=1的项目,返回对象
TProjectInfo project = projectDao.selectOne("select * from t_project_info where id = ? ", 1);

// 查询记录数
Integer count = projectDao.selectForObject("select count(*) from t_project_info order by created_at desc", Integer.class));

// 分页查询id>100的记录,第一页,每页10个
Page page = projectDao.paginate("select * from t_project_info order by created_at desc where id > ?", new Page(1, 10), 100));

// 查询id=3的项目信息列表
TProjectInfo project = new TProjectInfo();
project.

setId(3L);

List projectList = projectDao.select(project);

// 查询id=3的项目信息
TProjectInfo project = new TProjectInfo();
project.

setId(3L);

Project project = projectDao.selectOne(project);

// 查询id=3的项目信息
TProjectInfo project = projectDao.selectById(3L);

// 查询id=3和id=4的项目信息
List ids = new ArrayList();
ids.

add(3L);
ids.

add(4L);

TProjectInfo project = projectDao.selectByIds(ids)

// 查询id=3的项目信息
LambdaQueryCriteria criteria = new LambdaQueryCriteria<>().eq(Project::getId, 3L);
TProjectInfo project = projectDao.selectOne(criteria);

// 查询id=3的项目信息
QueryCriteria criteria = new QueryCriteria<>().eq("id", 3L);
TProjectInfo project = projectDao.selectOne(criteria);

// 分页查询id=3的项目信息,第一页,每页10个
TProjectInfo project = new TProjectInfo();
project.

setId(3L);

Page page = projectDao.paginate(project, new Page(1, 10));

// 分页查询id=3的项目信息,第一页,每页10个
QueryCriteria criteria = new QueryCriteria() <>.

eq("id",3L);

Page page = projectDao.paginate(criteria, new Page(1, 10));

// 根据条件构造器构建复杂查询条件
// 等价于 `SELECT id,project_name ,del_flag,created_by,updated_by,created_at AS createdAt,updated_at,remark FROM t_project_info WHERE created_by = 'admin' AND del_flag >= 0 AND id IN (1, 5) OR (remark NOT LIKE '%XXX%') ORDER BY created_at DESC`
List ids = new ArrayList<>();
ids.

add(1L);
ids.

add(5L);

LambdaQueryCriteria criteria = new LambdaQueryCriteria<>()
.eq(TProjectInfo::getCreatedBy, "admin")
.gte(TProjectInfo::getDelFlag, 0)
.in(TProjectInfo::getId, ids)
.or(i -> i.notLike(TProjectInfo::getRemark, "XXX"))
.orderBy(TProjectInfo::getCreatedAt, true);
List projectList = projectDao.select(criteria);

// 根据条件构造器构建复杂查询条件
// 等价于:SELECT id,project_name,enable_at,remark,del_flag,created_by,updated_by,created_at,updated_at FROM t_project_info WHERE (del_flag = 1 AND project_name = 'xxx') OR (del_flag = 1 AND project_name = 'yyy') ORDER BY id
List tProjectInfo2s = projectInfoDao.select(
new QueryCriteria()
.and(i -> i.eq("del_flag", 1)
.eq("project_name", "xxx")
)
.or(i -> i.eq("del_flag", 1)
.eq("project_name", "yyy")
)
.orderBy("id"));

// 根据条件构造器查询记录数量,等价于 `SELECT COUNT(*) FROM t_project_info WHERE id = 1695713712801116162`
LambdaQueryCriteria criteria = new LambdaQueryCriteria<>().eq(Project::getId, 1695713712801116162L);
Long count = projectDao.selectCount(criteria);

// 根据条件构造器查询记录是否存在,等价于 `SELECT COUNT(*) FROM t_project_info WHERE id = 1695713712801116162`,然后再判断结果count是否大于0
boolean result = projectDao.exists(CriteriaBuilder.lambdaQuery().eq(Project::getId, 1695713712801116162L));

```

2. 新增操作

```java

@Autowired
private ProjectDao projectDao;

// 使用sql插入一条数据
int result = projectDao.insert("insert t_into project_info(project_name, del_flag, remark) values (?,?,?)", "测试项目", 1, "XXXXXXX");

// 使用实体类插入一条数据,默认忽略null
TProjectInfo project = new TProjectInfo();
project.

setProjectName("xxxx");
project.

setDelFlag(1);
project.

setCreatedBy("admin");
project.

setRemark("XXXX");

int result = projectDao.insert(project);

// 获取生成的主键id,在主键策略为assignId、uuid或objectId时适用
Long id = project.getId();

// 使用实体类插入一条数据,不忽略null
int result = projectDao.insert(project, false);

// 获取生成的主键id,在主键策略为assignId、uuid或objectId时适用
Long id = project.getId();

// 使用实体类插入一条数据,并返回数据库自增的主键值,默认忽略null
int result = projectDao.insertReturnAutoIncrement(project);

```

3. 更新操作

```java

@Autowired
private ProjectDao projectDao;

// 使用sql插入一条数据
int result = baseDao.update(""update project_info set project_name = ? where
id=?"",new Object[]{"测试项目",1});

// 使用实体类更新一条数据,其中以主键Id值为where条件,默认忽略null,等价于 `update t_project_info set project_name='测试项目',del_flag=1,created_by='admin',remark='XXXX' where id=1`
TProjectInfo project = new TProjectInfo();
project.

setId(1);
project.

setProjectName("测试项目");
project.

setDelFlag(1);
project.

setCreatedBy("admin");
project.

setRemark("XXXX");

int result = baseDao.updateById(project);

// 使用实体类更新一条数据,不忽略null,等价于 `update t_project_info set project_name='测试项目',del_flag=1,created_by='admin',remark='XXXX',updated_by=NULL,created_at=NULL,updated_at=NULL where id=1`
int result = baseDao.updateById(project, false);

// 根据条件构造器(Lambda)作为条件更新一条数据,默认忽略null,等价于 `update t_project_info set project_name='测试项目' where id=1`
TProjectInfo project2 = new TProjectInfo();
project2.

setProjectName("测试项目");

LambdaUpdateCriteria criteria = new LambdaUpdateCriteria<>().eq(TProjectInfo::getId(),1L)
int result = baseDao.update(project2, criteria);

// 根据实体类内容和条件构造器作为条件更新一条数据,默认忽略null,等价于 `update t_project_info set project_name='测试项目' where id=1`
UpdateCriteria criteria = new UpdateCriteria<>().eq("id", 1L)
int result = update.update(project2, criteria);

// 根据实体类内容和条件构造器作为条件更新一条数据,不忽略null,等价于 `update t_project_info set project_name='测试项目',del_flag=NULL,created_by=NULL,remark=NULL,updated_by=NULL,created_at=NULL,updated_at=NULL where id=1`
UpdateCriteria criteria = new UpdateCriteria<>().eq("id", 1L)
int result = update.update(project2, false, criteria);

// 只根据条件构造器作为条件更新一条数据,等价于 `update t_project_info set project_name='测试项目' where id=1`
UpdateCriteria criteria = new UpdateCriteria<>().set("project_name", "测试项目").eq("id", 1L);
int result = baseDao.update(criteria);

// 只根据条件构造器(Lambda)作为条件更新一条数据,等价于 `update t_project_info set project_name='测试项目' where id=1`
LambdaUpdateCriteria criteria = new LambdaUpdateCriteria<>().set(TProjectInfo::getProjectName, "测试项目").eq("id", 1L);
int result = baseDao.update(criteria);
```

4. 删除操作

```java

@Autowired
private ProjectDao projectDao;

// 使用sql删除一条数据
int result = projectDao.delete(""delete from t_project_info where id = ? "", new Object[]{1});

// 根据实体类非null内容作为where条件删除一条数据,等价于 `delete from t_project_info where id = 1`
TProjectInfo project = new TProjectInfo();
project.

setId(1);

int result = baseDao.delete(project);

// 根据主键id删除一条数据,等价于 `delete from t_project_info where id = 1`
int result = baseDao.deleteById(1L);

// 根据主键id列表删除多条数据,等价于 `delete from t_project_info where id in (1, 5)`
List ids = new ArrayList();
ids.

add(1L);
ids.

add(5L);

int result = baseDao.deleteByIds(ids);

// 根据主键id数组删除多条数据,等价于 `delete from t_project_info where id in (1, 5)`
List ids = new ArrayList();
int result = baseDao.deleteByIds(1L, 5L);

// 根据条件构造器(Lambda)作为查询条件删除一条数据,等价于 `delete from t_project_info where id = 1`
LambdaUpdateCriteria criteria = new LambdaUpdateCriteria<>().eq(Project::getId(),1L)
int result = baseDao.delete(criteria);

// 根据条件构造器作为查询条件删除一条数据,等价于 `delete from t_project_info where id = 1`
UpdateCriteria criteria = new UpdateCriteria<>().eq("id", 1L)
int result = baseDao.delete(criteria);
```

## 11、安全使用说明

使用`QueryCriteria`和`UpdateCriteria`时应避免前端传入字段名,防止`SQL注入`的风险;
如若必须使用由前端传入的动态内容,如使用QueryCriteria.orderBy("任意前端传入字段")
进行动态排序,推荐使用工具类 `SqlInjectionUtils.check(内容)` 先行验证字符串是否存在 `SQL注入`, 存在则拒绝操作。

## 12、SQL日志打印分析

**该功能依赖 p6spy 组件,需进行配置后方可使用**

**注意!**

- driver-class-name 为 p6spy 提供的驱动类 com.p6spy.engine.spy.P6SpyDriver
- url 前缀为 jdbc:p6spy 跟着冒号为对应数据库连接地址
- 该插件有性能损耗,不建议在生产环境中使用。
- 更多参考 p6spy 官方文档: https://p6spy.readthedocs.io/en/latest/index.html

1. 引入 `p6spy` 依赖

```xml

p6spy
p6spy
最新版本

```

2. `application.yml` 配置,注意url的中间多了个p6spy

```yaml
spring:
datasource:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://127.0.0.1:3306/tiny_jdbc_test?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8
...
```

3. `spy.properties` 配置

请配置 `logMessageFormat=org.tinycloud.jdbc.p6spy.P6SpyLogger`

其余根据提示信息自定义修改

```properties
#################################################################
# P6Spy Options File #
# See documentation for detailed instructions #
# http://p6spy.github.io/p6spy/2.0/configandusage.html #
# https://p6spy.readthedocs.io/en/latest/configandusage.html #
#################################################################
#################################################################
# MODULES #
# #
# Module list adapts the modular functionality of P6Spy. #
# Only modules listed are active. #
# (default is com.p6spy.engine.logging.P6LogFactory and #
# com.p6spy.engine.spy.P6SpyFactory) #
# Please note that the core module (P6SpyFactory) can't be #
# deactivated. #
# Unlike the other properties, activation of the changes on #
# this one requires reload. #
#################################################################
#modulelist=com.p6spy.engine.spy.P6SpyFactory,com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
################################################################
# CORE (P6SPY) PROPERTIES #
################################################################
# A comma separated list of JDBC drivers to load and register.
# (default is empty)
#
# Note: This is normally only needed when using P6Spy in an
# application server environment with a JNDI data source or when
# using a JDBC driver that does not implement the JDBC 4.0 API
# (specifically automatic registration).
#driverlist=
# for flushing per statement
# (default is false)
#autoflush=false
# sets the date format using Java's SimpleDateFormat routine.
# In case property is not set, milliseconds since 1.1.1970 (unix time) is used (default is empty)
#dateformat=
# prints a stack trace for every statement logged
#stacktrace=false
# if stacktrace=true, specifies the stack trace to print
#stacktraceclass=
# determines if property file should be reloaded
# Please note: reload means forgetting all the previously set
# settings (even those set during runtime - via JMX)
# and starting with the clean table
# (default is false)
#reloadproperties=false
# determines how often should be reloaded in seconds
# (default is 60)
#reloadpropertiesinterval=60
# specifies the appender to use for logging
# Please note: reload means forgetting all the previously set
# settings (even those set during runtime - via JMX)
# and starting with the clean table
# (only the properties read from the configuration file)
# (default is com.p6spy.engine.spy.appender.FileLogger)
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
appender=com.p6spy.engine.spy.appender.StdoutLogger
#appender=com.p6spy.engine.spy.appender.FileLogger
# name of logfile to use, note Windows users should make sure to use forward slashes in their pathname (e:/test/spy.log)
# (used for com.p6spy.engine.spy.appender.FileLogger only)
# (default is spy.log)
#logfile=spy.log
# append to the p6spy log file. if this is set to false the
# log file is truncated every time. (file logger only)
# (default is true)
#append=true
# class to use for formatting log messages (default is: com.p6spy.engine.spy.appender.SingleLineFormat)
logMessageFormat=org.tinycloud.jdbc.p6spy.P6SpyLogger
# Custom log message format used ONLY IF logMessageFormat is set to com.p6spy.engine.spy.appender.CustomLineFormat
# default is %(currentTime)|%(executionTime)|%(category)|connection%(connectionId)|%(sqlSingleLine)
# Available placeholders are:
# %(connectionId) the id of the connection
# %(currentTime) the current time expressing in milliseconds
# %(executionTime) the time in milliseconds that the operation took to complete
# %(category) the category of the operation
# %(effectiveSql) the SQL statement as submitted to the driver
# %(effectiveSqlSingleLine) the SQL statement as submitted to the driver, with all new lines removed
# %(sql) the SQL statement with all bind variables replaced with actual values
# %(sqlSingleLine) the SQL statement with all bind variables replaced with actual values, with all new lines removed
#customLogMessageFormat=%(currentTime)|%(executionTime)|%(category)|connection%(connectionId)|%(sqlSingleLine)
# format that is used for logging of the java.util.Date implementations (has to be compatible with java.text.SimpleDateFormat)
# (default is yyyy-MM-dd'T'HH:mm:ss.SSSZ)
#databaseDialectDateFormat=yyyy-MM-dd'T'HH:mm:ss.SSSZ
# format that is used for logging of the java.sql.Timestamp implementations (has to be compatible with java.text.SimpleDateFormat)
# (default is yyyy-MM-dd'T'HH:mm:ss.SSSZ)
#databaseDialectTimestampFormat=yyyy-MM-dd'T'HH:mm:ss.SSSZ
# format that is used for logging booleans, possible values: boolean, numeric
# (default is boolean)
#databaseDialectBooleanFormat=boolean
# Specifies the format for logging binary data. Not applicable if excludebinary is true.
# (default is com.p6spy.engine.logging.format.HexEncodedBinaryFormat)
#databaseDialectBinaryFormat=com.p6spy.engine.logging.format.PostgreSQLBinaryFormat
#databaseDialectBinaryFormat=com.p6spy.engine.logging.format.MySQLBinaryFormat
#databaseDialectBinaryFormat=com.p6spy.engine.logging.format.HexEncodedBinaryFormat
# whether to expose options via JMX or not
# (default is true)
#jmx=true
# if exposing options via jmx (see option: jmx), what should be the prefix used?
# jmx naming pattern constructed is: com.p6spy(.)?:name=
# please note, if there is already such a name in use it would be unregistered first (the last registered wins)
# (default is none)
#jmxPrefix=
# if set to true, the execution time will be measured in nanoseconds as opposed to milliseconds
# (default is false)
#useNanoTime=false
#################################################################
# DataSource replacement #
# #
# Replace the real DataSource class in your application server #
# configuration with the name com.p6spy.engine.spy.P6DataSource #
# (that provides also connection pooling and xa support). #
# then add the JNDI name and class name of the real #
# DataSource here #
# #
# Values set in this item cannot be reloaded using the #
# reloadproperties variable. Once it is loaded, it remains #
# in memory until the application is restarted. #
# #
#################################################################
#realdatasource=/RealMySqlDS
#realdatasourceclass=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
#################################################################
# DataSource properties #
# #
# If you are using the DataSource support to intercept calls #
# to a DataSource that requires properties for proper setup, #
# define those properties here. Use name value pairs, separate #
# the name and value with a semicolon, and separate the #
# pairs with commas. #
# #
# The example shown here is for mysql #
# #
#################################################################
#realdatasourceproperties=port;3306,serverName;myhost,databaseName;jbossdb,foo;bar
#################################################################
# JNDI DataSource lookup #
# #
# If you are using the DataSource support outside of an app #
# server, you will probably need to define the JNDI Context #
# environment. #
# #
# If the P6Spy code will be executing inside an app server then #
# do not use these properties, and the DataSource lookup will #
# use the naming context defined by the app server. #
# #
# The two standard elements of the naming environment are #
# jndicontextfactory and jndicontextproviderurl. If you need #
# additional elements, use the jndicontextcustom property. #
# You can define multiple properties in jndicontextcustom, #
# in name value pairs. Separate the name and value with a #
# semicolon, and separate the pairs with commas. #
# #
# The example shown here is for a standalone program running on #
# a machine that is also running JBoss, so the JNDI context #
# is configured for JBoss (3.0.4). #
# #
# (by default all these are empty) #
#################################################################
#jndicontextfactory=org.jnp.interfaces.NamingContextFactory
#jndicontextproviderurl=localhost:1099
#jndicontextcustom=java.naming.factory.url.pkgs;org.jboss.naming:org.jnp.interfaces
#jndicontextfactory=com.ibm.websphere.naming.WsnInitialContextFactory
#jndicontextproviderurl=iiop://localhost:900
################################################################
# P6 LOGGING SPECIFIC PROPERTIES #
################################################################
# filter what is logged
# please note this is a precondition for usage of: include/exclude/sqlexpression
# (default is false)
#filter=false
# comma separated list of strings to include
# please note that special characters escaping (used in java) has to be done for the provided regular expression
# (default is empty)
#include=
# comma separated list of strings to exclude
# (default is empty)
#exclude=
# sql expression to evaluate if using regex
# please note that special characters escaping (used in java) has to be done for the provided regular expression
# (default is empty)
#sqlexpression=
#list of categories to exclude: error, info, batch, debug, statement,
#commit, rollback, result and resultset are valid values
# (default is info,debug,result,resultset,batch)
#excludecategories=info,debug,result,resultset,batch
#whether the binary values (passed to DB or retrieved ones) should be logged with placeholder: [binary] or not.
# (default is false)
#excludebinary=false
# Execution threshold applies to the standard logging of P6Spy.
# While the standard logging logs out every statement
# regardless of its execution time, this feature puts a time
# condition on that logging. Only statements that have taken
# longer than the time specified (in milliseconds) will be
# logged. This way it is possible to see only statements that
# have exceeded some high water mark.
# This time is reloadable.
#
# executionThreshold=integer time (milliseconds)
# (default is 0)
#executionThreshold=
################################################################
# P6 OUTAGE SPECIFIC PROPERTIES #
################################################################
# Outage Detection
#
# This feature detects long-running statements that may be indicative of
# a database outage problem. If this feature is turned on, it will log any
# statement that surpasses the configurable time boundary during its execution.
# When this feature is enabled, no other statements are logged except the long
# running statements. The interval property is the boundary time set in seconds.
# For example, if this is set to 2, then any statement requiring at least 2
# seconds will be logged. Note that the same statement will continue to be logged
# for as long as it executes. So if the interval is set to 2, and the query takes
# 11 seconds, it will be logged 5 times (at the 2, 4, 6, 8, 10 second intervals).
#
# outagedetection=true|false
# outagedetectioninterval=integer time (seconds)
#
# (default is false)
#outagedetection=false
# (default is 60)
#outagedetectioninterval=30
```

## 13、许可证

[Apache License 2.0](https://github.com/llllllxy/tiny-jdbc/blob/master/LICENSE) 免费用于个人和商业,请放心使用