数据库访问
# 1 简介
数据库访问是平台提供的访问数据库的接口DB。它的基本用法与JDBC相似,但是它提供更加统一方便的接口。在使用DB访问数据库的时候统一使用KSQL语法。在苍穹系统中,支持每个应用使用独立的数据库,SQL在哪个数据库上执行是通过DBRoute来路由。DB屏蔽了物理数据库信息,用DBRoute对应具体的物理库,由mc的数据中心管理进行配置。DBRoute内置了常用的routeKey,也可以通过DBRoute.of("routeKey")来构造。表单的routeKey在设计时已自动指定,在同一个应用下的表单routeKey相同。
# 2 应用场景
在需要通过SQL访问的场景下必须使用DB来访问数据库。
# 3 接口说明
数据库访问DB相关接口定义和实现存在于bos-dbengine-1.0.jar中。对于使用者来说只需要调用kd.bos.db.DB类中的方法即可。
## 3.1 接口列表
| 方法 | 说明 |
| - | - |
| query | 查询 |
| queryDataSet | 查询(返回DataSet) |
| update | 插入、更新或删除 |
| execute | 执行任何类型的SQL |
| executeBatch | 批量执行任何类型的SQL |
| timeout | 设置超时时间 |
| getDBType | 获取数据库类型 |
| exitsTable | 判断表是否存在 |
| exitsIndex | 判断表索引是否存在 |
| getPrimaryKeys | 获取表主键 |
| getFieldInfo | 获取表的字段信息 |
| getIndexInfo | 获取表的索引信息 |
| isXDBEnable | 是否启用了xdb引擎(水平分表) |
| isSplittingReadEnable | 是否开启读写分离 |
| isSplittingAutoReadWrite | 是否开启自动读写模式 |
| getWrittenRouteKey | 获取当前事务已写的库路由标识 |
## 3.2 接口详情
### query
+ **功能描述**
查询数据库操作。
+ **方法**
```java
public static <T> T query(DBRoute dbRoute, String sql, ResultSetHandler<T> rh)
public static <T> T query(DBRoute dbRoute, String sql, Object[] params, ResultSetHandler<T> rh)
```
+ **参数说明**
| 参数 | 类型 | 说明 |
| - | - | - |
| dbRoute | DBRoute | 数据库路由 |
| sql | String | 查询SQL语句 |
| params | Object[] | SQL参数 |
| rh | ResultSetHandler\<T> | jdbc结果集ResultSet处理器 |
+ **返回值**
返回ResultSet处理器返回的结果。
+ **示例代码**
```java
// 不带参数
String sql = "SELECT * FROM table;"
DB.query(DBRoute.basedata, sql, rs -> {
return rs.next();
});
// 带参数
String sql = "SELECT * FROM table WHERE fid=?;"
DB.query(DBRoute.basedata, sql, new Object[]{"xxxxxxxx"}, rs -> {
return rs.next();
});
```
### queryDataSet
+ **功能描述**
查询数据库操作(返回DataSet)。
+ **方法**
```java
public static DataSet queryDataSet(String algoKey, DBRoute dbRoute, String sql)
public static DataSet queryDataSet(String algoKey, DBRoute dbRoute, String sql, Object[] params)
```
+ **参数说明**
| 参数 | 类型 | 说明 |
| - | - | - |
| algoKey | String | algo标识 |
| dbRoute | DBRoute | 数据库路由 |
| sql | String | 查询SQL语句 |
| params | Object[] | SQL参数 |
> 参数algoKey用于跟踪性能,当前执行的时间将被记录到指标系统。这个值,应能有效定位到代码, 如=类名+方法,至少含类名。
+ **返回值**
返回DataSet数据集(它是根据查询结果ResultSet包装而来)。
+ **示例代码**
```java
// 不带参数
String sql = "SELECT * FROM table;"
DataSet dataSet = DB.queryDataSet(this.getClass.getName(), DBRoute.basedata, sql);
// 带参数
String sql = "SELECT * FROM table WHERE fid=?;"
DataSet dataSet = DB.queryDataSet(this.getClass.getName(), DBRoute.basedata, sql, new Object[]{"xxxxxxxx"});
```
### update
+ **功能描述**
插入、更新或删除数据库操作。
+ **方法**
```java
public static int update(DBRoute dbRoute, String sql)
public static int update(DBRoute dbRoute, String sql, Object[] params)
```
+ **参数说明**
| 参数 | 类型 | 说明 |
| - | - | - |
| dbRoute | DBRoute | 数据库路由 |
| sql | String | 查询SQL语句 |
| params | Object[] | SQL参数 |
+ **返回值**
更新数据的行数。
+ **示例代码**
```java
// 不带参数
String sql = "UPDATE t_xxx SET field=xxx WHERE fid=xxx;"
DB.update(DBRoute.basedata, sql);
// 带参数
String sql = "UPDATE t_xxx SET field=? WHERE fid=?;"
DB.update(DBRoute.basedata, sql, new Object[]{"xxx", "xxx"});
```
### execute
+ **功能描述**
执行任何类型的SQL。
+ **方法**
```java
public static boolean execute(DBRoute dbRoute, String sql)
public static boolean execute(DBRoute dbRoute, String sql, Object[] params)
```
+ **参数说明**
| 参数 | 类型 | 说明 |
| - | - | - |
| dbRoute | DBRoute | 数据库路由 |
| sql | String | 查询SQL语句 |
| params | Object[] | SQL参数 |
+ **返回值**
是否执行成功。
+ **示例代码**
```java
// 不带参数
String sql = "UPDATE t_xxx SET field=xxx WHERE fid=xxx;"
DB.execute(DBRoute.basedata, sql);
// 带参数
String sql = "UPDATE t_xxx SET field=? WHERE fid=?;"
DB.execute(DBRoute.basedata, sql, new Object[]{"xxx", "xxx"});
```
### executeBatch
+ **功能描述**
批量执行任何类型的SQL。
+ **方法**
```java
public static boolean execute(DBRoute dbRoute, String sql, List<Object[]> paramsList)
```
+ **参数说明**
| 参数 | 类型 | 说明 |
| - | - | - |
| dbRoute | DBRoute | 数据库路由 |
| sql | String | 查询SQL语句 |
| paramsList | List<Object[]> | SQL参数列表 |
+ **返回值**
批量执行的数量。
+ **示例代码**
```java
String sql = "UPDATE t_xxx SET field=? WHERE fid=?;";
List<Object[]> paramsList = new ArrayList();
Object[] params = new Object[]{"xxx", "xxx"};
paramsList.add(params);
int[] executeBatch = DB.executeBatch(DBRoute.basedata, sql, paramsList);
```
### timeout
+ **功能描述**
设置查询超时时间。
+ **方法**
```java
public static QueryTimeout timeout(int seconds)
```
+ **参数说明**
| 参数 | 类型 | 说明 |
| - | - | - |
| seconds | int | 超时时间(秒) |
+ **返回值**
查询超时对象。
+ **示例代码**
```java
try(DB.timeout(10)) {
DB.query(...);
}
```
> 默认情况下,执行SQL超时时间为300秒(可通过在MC配置db.query.timeout=300来修改)。timeout方法允许开发者自定义查询超时时间。执行超过query time,则执行中断并抛出SQLException。
# 4 注意事项
## 4.1 ResultSetHandler
```java
public interface ResultSetHandler<T> {
T handle(ResultSet rs) throws Exception;
}
```
ResultSetHandler是用于处理JDBC结果集ResultSet的一个自定义的处理器接口。使用者可以实现该接口方法来实现结果集的处理逻辑,实现的方法中的返回值也是DB.query方法的返回值。
## 4.2 优化in参数转临时表
在数据库操作中,由于数据库对in参数有长度限制,所以我们需要对in参数转临时表处理。
当启用in优化,in的参数个数>=优化阀值时,将对过滤条件信息优化。in参数个数超过orm.opt.in.maxsize,则系统抛异常,异常信息形如:
> in query params length over the maximum:510000 > 500000
+ in优化参数配置
```properties
# 优化阀值:默认1000
orm.opt.in.threshold=1000
# in允许的最大个数:默认50w
orm.opt.in.maxsize=500000
```
为了优化in参数转临时表,在DB中提供了专门的in参数转临时表数据库操作接口。
```java
public static boolean execute(DBRoute dbRoute, SqlBuilder sb)
public static <T> T query(DBRoute dbRoute, SqlBuilder sb, ResultSetHandler<T> rh)
public static DataSet queryDataSet(String algoKey, DBRoute dbRoute, SqlBuilder sb)
```
这些接口与其他的数据库操作接口使用并无太大差别。在有需要in参数转临时表时,使用上述三个接口,只是这些接口需要使用SqlBuilder来创建SQL语句。如下例所示:
```java
public void testDBQuerySqlBuilder(){
//设置启用阀值
System.setProperty("orm.opt.in.threshold", "100");
List<Object> pkIds = getPKs(100);
//oraginal sql= SELECT FID FROM T_SEC_USER WHERE FID IN(?,?,?...) ORDER BY FID DESC
SqlBuilder sqlBuilder = new SqlBuilder();
sqlBuilder.append("SELECT FID FROM T_SEC_USER WHERE");
sqlBuilder.appendIn("FID", pkIds);
sqlBuilder.append("ORDER BY FID DESC");
DataSet ds = DB.queryDataSet("test_sqlBuilder", DBRoute.basedata, sqlBuilder);
}
public List<Object> getPKs(int size){
String sql = "SELECT TOP " + size + ",0 FID FROM T_SEC_USER";
DataSet ds = DB.queryDataSet("InQueryToTempTable_getPKS", DBRoute.base, sql);
List<Object> list = new ArrayList<>();
for(Row r:ds){
Object o = r.get(0);
list.add(o);
}
return list;
}
```
## 4.3 空格和0长度串
数据库字段若为字符串类型,则不允许保存null和0长度的字符串,无值默认用" "。
用户仍正常使用,存取值由DB层处理转换:
+ 保存:参数传入"“或null,则保存为” "。 必须通过参数设置才会自动转换。(ParameterSetter)
+ 查询:" “转为”"。 通过DB结果集转换。(WrapCloseResultSet)
控制是否转换的参数:db.spaceAsEmptyString,默认值为true。
## 4.4 bigint类型值
ksql bigint类型的字段,取值返回类型统一 为long。
## 4.5 boolean类型参数
参数传boolean类型,则在jdbc参数设置时自动转为’0’或’1’。(对应的存储字段类型应为char(1))
> 注意:在ResultSet中取值仍为’0’或’1’,在ORM的DynamicObject中的值则为true或false。
## 4.6 禁止新建线程访问
**若需要开启新线程,用ThreadPools启动。自行new Thread被禁止使用DB访问数据库。**
## 4.7 开发环境的SQL日志
在开发环境,如果info级别的日志被关闭,则可以通过以下代码,在控制台输出。
```java
// 启动前开启DB日志 (加在启动类里)
System.setProperty("db.sql.out", "true");
// 运行期前开启DB日志(仅用于调试,不要在产品中使用,会导致日志配置失效)
DB.setSqlOut(true);
```
***SQL跟踪调试***
1. 在AbstractDBImpl.logSQL方法中设置断点。
2. 为方便对sql进行编码式过滤调试,提供了自定义SqlLogger。在启动类 start之前,增加InitListener,设置SqlLogger(见下面代码),把SQL再次log出来过滤。不受db.sql.out参数的控制,只要设置了SqlLogger,日志必输出,不影响原有的日志输出。
```java
webInit.addInitListener(new InitListener() {
@Override
public void started() {
DB.setSqlLogger(new SqlLogger() {
@Override
public void log(String sql, Object...params) {
if (sql.toLowerCase().indexOf("t_sec_user") != -1) {
// debug,在这里设置断点进行调试。
System.err.println("### 用户表sql: " + sql);
} else {
System.out.println("### SQL: " + sql);
}
}
});
}
});
webInit.start();
```
***上例输出***
> \### 用户表sql: /*ORM*/ SELECT B.fdptid “entryentity.dpt.id”FROM t_SEC_User A LEFT JOIN t_SEC_UserPosition B ON B.FId=A.FId WHERE A.FId = ?
数据库访问
# 1 简介数据库访问是平台提供的访问数据库的接口DB。它的基本用法与JDBC相似,但是它提供更加统一方便的接口。在使用DB访问数据库的时候...
点击下载文档
本文2024-09-23 00:27:58发表“云苍穹知识”栏目。
本文链接:https://wenku.my7c.com/article/kingdee-cangqiong-139637.html
您需要登录后才可以发表评论, 登录登录 或者 注册
最新文档
热门文章