mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
commit
02cd825a2d
@ -185,5 +185,11 @@
|
|||||||
<version>23.5.0.24.07</version>
|
<version>23.5.0.24.07</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.sap.cloud.db.jdbc</groupId>
|
||||||
|
<artifactId>ngdbc</artifactId>
|
||||||
|
<version>2.24.6</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -62,6 +62,8 @@ public class DialectFactory implements DriverNamePool {
|
|||||||
return new PhoenixDialect();
|
return new PhoenixDialect();
|
||||||
} else if (DRIVER_DM7.equalsIgnoreCase(driverName)) {
|
} else if (DRIVER_DM7.equalsIgnoreCase(driverName)) {
|
||||||
return new DmDialect();
|
return new DmDialect();
|
||||||
|
} else if (DRIVER_HANA.equalsIgnoreCase(driverName)) {
|
||||||
|
return new HanaDialect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 无法识别可支持的数据库类型默认使用ANSI方言,可兼容大部分SQL语句
|
// 无法识别可支持的数据库类型默认使用ANSI方言,可兼容大部分SQL语句
|
||||||
@ -168,6 +170,9 @@ public class DialectFactory implements DriverNamePool {
|
|||||||
} else if (nameContainsProductInfo.contains("goldendb")) {
|
} else if (nameContainsProductInfo.contains("goldendb")) {
|
||||||
// GoldenDB
|
// GoldenDB
|
||||||
driver = DRIVER_GOLDENDB;
|
driver = DRIVER_GOLDENDB;
|
||||||
|
} else if (nameContainsProductInfo.contains("sap")) {
|
||||||
|
// sap hana
|
||||||
|
driver = DRIVER_HANA;
|
||||||
}
|
}
|
||||||
|
|
||||||
return driver;
|
return driver;
|
||||||
|
@ -9,7 +9,7 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
* @author Looly
|
* @author Looly
|
||||||
*/
|
*/
|
||||||
public enum DialectName {
|
public enum DialectName {
|
||||||
ANSI, MYSQL, ORACLE, POSTGRESQL, SQLITE3, H2, SQLSERVER, SQLSERVER2012, PHOENIX, DM;
|
ANSI, MYSQL, ORACLE, POSTGRESQL, SQLITE3, H2, SQLSERVER, SQLSERVER2012, PHOENIX, DM, HANA;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否为指定数据库方言,检查时不分区大小写
|
* 是否为指定数据库方言,检查时不分区大小写
|
||||||
|
@ -120,4 +120,8 @@ public interface DriverNamePool {
|
|||||||
* JDBC 驱动 GoldenDB
|
* JDBC 驱动 GoldenDB
|
||||||
*/
|
*/
|
||||||
String DRIVER_GOLDENDB = "com.goldendb.jdbc.Driver";
|
String DRIVER_GOLDENDB = "com.goldendb.jdbc.Driver";
|
||||||
|
/**
|
||||||
|
* JDBC 驱动 Sap Hana
|
||||||
|
*/
|
||||||
|
String DRIVER_HANA = "com.sap.db.jdbc.Driver";
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,93 @@
|
|||||||
|
package cn.hutool.db.dialect.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.db.Entity;
|
||||||
|
import cn.hutool.db.Page;
|
||||||
|
import cn.hutool.db.StatementUtil;
|
||||||
|
import cn.hutool.db.dialect.DialectName;
|
||||||
|
import cn.hutool.db.sql.SqlBuilder;
|
||||||
|
import cn.hutool.db.sql.Wrapper;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hana数据库方言
|
||||||
|
*
|
||||||
|
* @author daoyou.dev
|
||||||
|
*/
|
||||||
|
public class HanaDialect extends AnsiSqlDialect {
|
||||||
|
|
||||||
|
public HanaDialect() {
|
||||||
|
wrapper = new Wrapper('"');
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String dialectName() {
|
||||||
|
return DialectName.HANA.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SqlBuilder wrapPageSql(SqlBuilder find, Page page) {
|
||||||
|
// SAP HANA 使用 OFFSET LIMIT 分页
|
||||||
|
return find.append(" LIMIT ").append(page.getPageSize())
|
||||||
|
.append(" OFFSET ").append(page.getStartPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建用于upsert的{@link PreparedStatement}。
|
||||||
|
* SAP HANA 使用 MERGE INTO 语法来实现 UPSERT 操作。
|
||||||
|
* <p>
|
||||||
|
* 生成 SQL 语法为:
|
||||||
|
* <pre>
|
||||||
|
* MERGE INTO demo AS target
|
||||||
|
* USING (SELECT ? AS a, ? AS b, ? AS c FROM DUMMY) AS source
|
||||||
|
* ON target.id = source.id
|
||||||
|
* WHEN MATCHED THEN
|
||||||
|
* UPDATE SET target.a = source.a, target.b = source.b, target.c = source.c
|
||||||
|
* WHEN NOT MATCHED THEN
|
||||||
|
* INSERT (a, b, c) VALUES (source.a, source.b, source.c);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param conn 数据库连接对象
|
||||||
|
* @param entity 数据实体类(包含表名)
|
||||||
|
* @param keys 主键字段数组,通常用于确定匹配条件(联合主键)
|
||||||
|
* @return PreparedStatement
|
||||||
|
* @throws SQLException SQL 执行异常
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public PreparedStatement psForUpsert(Connection conn, Entity entity, String... keys) throws SQLException {
|
||||||
|
SqlBuilder.validateEntity(entity);
|
||||||
|
final SqlBuilder builder = SqlBuilder.create(wrapper);
|
||||||
|
|
||||||
|
final List<String> columns = new ArrayList<>();
|
||||||
|
final List<Object> values = new ArrayList<>();
|
||||||
|
|
||||||
|
// 构建字段部分和参数占位符部分
|
||||||
|
entity.forEach((field, value) -> {
|
||||||
|
if (StrUtil.isNotBlank(field)) {
|
||||||
|
columns.add(wrapper != null ? wrapper.wrap(field) : field);
|
||||||
|
values.add(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
String tableName = entity.getTableName();
|
||||||
|
if (wrapper != null) {
|
||||||
|
tableName = wrapper.wrap(tableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建 UPSERT 语句
|
||||||
|
builder.append("UPSERT ").append(tableName).append(" (");
|
||||||
|
builder.append(String.join(", ", columns));
|
||||||
|
builder.append(") VALUES (");
|
||||||
|
builder.append(String.join(", ", Collections.nCopies(columns.size(), "?")));
|
||||||
|
builder.append(") WITH PRIMARY KEY");
|
||||||
|
|
||||||
|
return StatementUtil.prepareStatement(conn, builder.toString(), values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
89
hutool-db/src/test/java/cn/hutool/db/HanaTest.java
Normal file
89
hutool-db/src/test/java/cn/hutool/db/HanaTest.java
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package cn.hutool.db;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Console;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hana操作单元测试
|
||||||
|
*
|
||||||
|
* @author daoyou.dev
|
||||||
|
*/
|
||||||
|
public class HanaTest {
|
||||||
|
@BeforeAll
|
||||||
|
public static void createTable() throws SQLException {
|
||||||
|
Db db = Db.use("hana");
|
||||||
|
long count = db.count("SELECT * FROM SYS.TABLES WHERE TABLE_NAME = ? AND SCHEMA_NAME = CURRENT_SCHEMA", "user");
|
||||||
|
if (count > 0) {
|
||||||
|
db.execute("drop table \"user\"");
|
||||||
|
}
|
||||||
|
db.execute("CREATE COLUMN TABLE \"user\" (\"id\" INT NOT NULL, \"account\" VARCHAR(255), \"name\" VARCHAR(255), \"text\" VARCHAR(255), \"test1\" VARCHAR(255), \"pass\" VARCHAR(255), PRIMARY KEY (\"id\"))");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Disabled
|
||||||
|
public void insertTest() throws SQLException {
|
||||||
|
DbUtil.setReturnGeneratedKeyGlobal(false);
|
||||||
|
for (int id = 100; id < 200; id++) {
|
||||||
|
Db.use("hana").insert(Entity.create("user")//
|
||||||
|
.set("id", id)//
|
||||||
|
.set("name", "测试用户" + id)//
|
||||||
|
.set("text", "描述" + id)//
|
||||||
|
.set("test1", "t" + id)//
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事务测试<br>
|
||||||
|
* 更新三条信息,低2条后抛出异常,正常情况下三条都应该不变
|
||||||
|
*
|
||||||
|
* @throws SQLException SQL异常
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Disabled
|
||||||
|
public void txTest() throws SQLException {
|
||||||
|
Db.use("hana").tx(db -> {
|
||||||
|
int update = db.update(Entity.create("user").set("text", "描述100"), Entity.create().set("id", 100));
|
||||||
|
db.update(Entity.create("user").set("text", "描述101"), Entity.create().set("id", 101));
|
||||||
|
if (1 == update) {
|
||||||
|
// 手动指定异常,然后测试回滚触发
|
||||||
|
throw new RuntimeException("Error");
|
||||||
|
}
|
||||||
|
db.update(Entity.create("user").set("text", "描述102"), Entity.create().set("id", 102));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Disabled
|
||||||
|
public void pageTest() throws SQLException {
|
||||||
|
PageResult<Entity> result = Db.use("hana").page(Entity.create("\"user\""), new Page(2, 10));
|
||||||
|
for (Entity entity : result) {
|
||||||
|
Console.log(entity.get("id"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Disabled
|
||||||
|
public void getTimeStampTest() throws SQLException {
|
||||||
|
final List<Entity> all = Db.use("hana").findAll("user");
|
||||||
|
Console.log(all);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void upsertTest() throws SQLException {
|
||||||
|
DbUtil.setReturnGeneratedKeyGlobal(false);
|
||||||
|
Db db = Db.use("hana");
|
||||||
|
db.insert(Entity.create("user").set("id", 1).set("account", "ice").set("pass", "123456"));
|
||||||
|
db.upsert(Entity.create("user").set("id", 1).set("account", "daoyou").set("pass", "a123456").set("name", "道友"));
|
||||||
|
Entity user = db.get(Entity.create("user").set("id", 1));
|
||||||
|
System.out.println("user=======" + user.getStr("account") + "___" + user.getStr("pass"));
|
||||||
|
assertEquals("daoyou", user.getStr("account"));
|
||||||
|
}
|
||||||
|
}
|
@ -77,3 +77,9 @@ user = SYSDBA
|
|||||||
pass = 123456789
|
pass = 123456789
|
||||||
remarks = true
|
remarks = true
|
||||||
|
|
||||||
|
[hana]
|
||||||
|
url = jdbc:sap://127.0.0.1:30015/HAP_CONN?autoReconnect=true
|
||||||
|
user = DB
|
||||||
|
pass = 123456
|
||||||
|
remarks = true
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user