常见问题(FAQ)
本文档收集了 Unabo 框架使用过程中的常见问题和解决方案。
目录
安装与配置
Q1: 如何添加 Unabo 依赖?
A: Maven 项目添加以下依赖:
<dependency>
<groupId>online.sanen</groupId>
<artifactId>unabo</artifactId>
<version>1.2.4</version>
</dependency>
Gradle 项目:
implementation 'online.sanen:unabo:1.2.4'
Q2: 需要哪些依赖?
A: 除了 Unabo 本身,还需要:
数据库驱动(必选):根据使用的数据库选择
- MySQL:
mysql-connector-java - PostgreSQL:
postgresql - Oracle:
ojdbc6 - SQL Server:
jtds - SQLite:
sqlite-jdbc(已包含) - 达梦:
DmJdbcDriver18 - MongoDB:
mongodb-driver-sync
- MySQL:
连接池(可选,默认使用 Dbcp)
- HikariCP(推荐):
HikariCP - Druid:
druid - C3P0:
c3p0
- HikariCP(推荐):
Spring Boot 集成(可选)
spring-data-jdbc(用于 Spring 事务)
Q3: 如何关闭 SQL 日志?
A: 在配置中设置:
Bootstrap bootstrap = Unabo.load(configuration -> {
configuration.setUrl("jdbc:mysql://localhost:3306/test");
configuration.setDriverOption(DriverOption.MYSQL_CJ);
configuration.setIsShowSql(false); // 关闭 SQL 日志
});
或在 application.yml 中:
unabo:
sql-instances:
- id: sys
show-log: false # 关闭日志
Q4: 如何配置连接池参数?
A: 方式一:使用配置对象
Bootstrap bootstrap = Unabo.load(configuration -> {
configuration.setUrl("jdbc:mysql://localhost:3306/test");
configuration.setDriverOption(DriverOption.MYSQL_CJ);
configuration.setUsername("root");
configuration.setPassword("123456");
configuration.setDatasourceType(Configuration.DatasourceType.HikariCP);
configuration.setMaxActive(50); // 最大连接数
});
方式二:获取连接池实例深度配置
import com.zaxxer.hikari.HikariDataSource;
HikariDataSource dataSource = (HikariDataSource) bootstrap.manager()
.getTemplate()
.getDataSource();
dataSource.setMaximumPoolSize(50);
dataSource.setMinimumIdle(10);
基本使用
Q5: 如何创建 Bootstrap 实例?
A: 三种方式:
方式一:无 ID(不缓存)
Bootstrap bootstrap = Unabo.load(configuration -> {
configuration.setUrl("jdbc:mysql://localhost:3306/test");
configuration.setDriverOption(DriverOption.MYSQL_CJ);
configuration.setUsername("root");
configuration.setPassword("123456");
});
方式二:有 ID(缓存)
Bootstrap bootstrap = Unabo.load("sys", configuration -> {
// 配置...
});
方式三:try-with-resources(推荐)
try (Bootstrap bootstrap = Unabo.load(configuration -> {
// 配置...
})) {
// 使用 bootstrap
} // 自动关闭
Q6: 如何创建表?
A: 方式一:使用 Map
Map<String, Object> schema = new HashMap<>();
schema.put("id", 1);
schema.put("name", "");
schema.put("age", 0);
bootstrap.queryMap("user", schema).setPrimary("id").create();
方式二:使用实体类
@Table(name = "user")
public class User {
@Id
private Integer id;
private String name;
private Integer age;
}
bootstrap.query(new User()).createTable();
Q7: 如何指定字段类型?
A: 在 Map 中设置对应类型的示例值:
Map<String, Object> schema = new HashMap<>();
schema.put("id", 1); // Integer
schema.put("name", ""); // String
schema.put("age", 0); // Integer
schema.put("price", 0.0); // Double
schema.put("active", true); // Boolean
schema.put("created_at", new Date()); // Date
bootstrap.queryMap("product", schema).setPrimary("id").create();
Q8: 如何插入数据?
A: 方式一:使用 Map
Map<String, Object> user = new HashMap<>();
user.put("name", "张三");
user.put("age", 25);
bootstrap.queryMap("user", user).insert();
方式二:使用实体类
User user = new User();
user.setName("李四");
user.setAge(30);
bootstrap.query(user).insert();
方式三:批量插入
List<User> users = Arrays.asList(user1, user2, user3);
bootstrap.query(users).insert();
查询相关
Q9: 如何查询所有数据?
A:
// 返回 Map 列表
List<Map<String, Object>> users = bootstrap.queryTable("user").maps();
// 返回实体列表
List<User> users = bootstrap.queryTable("user").list(User.class);
Q10: 如何根据条件查询?
A:
import online.sanen.unabo.api.condition.C;
// 单条件
List<User> users = bootstrap.queryTable("user")
.addCondition(C.eq("age", 25))
.list(User.class);
// 多条件(AND)
List<User> users = bootstrap.queryTable("user")
.addCondition(C.eq("age", 25))
.addCondition(C.like("name", "张%"))
.list(User.class);
// OR 条件
List<User> users = bootstrap.queryTable("user")
.addCondition(C.or(
C.eq("age", 25),
C.eq("age", 30)
))
.list(User.class);
Q11: 如何分页查询?
A:
import com.mhdt.structure.Page;
int currentPage = 1;
int pageSize = 10;
// 查询总数
int total = bootstrap.queryTable("user").count();
// 创建分页对象
Page page = new Page(currentPage, pageSize);
page.setCount(total);
// 分页查询
List<User> users = bootstrap.queryTable("user")
.limit(page.skip(), pageSize)
.sort(Sorts.DESC, "id")
.list(User.class);
Q12: 如何只查询部分字段?
A:
// 指定查询字段
List<Map<String, Object>> users = bootstrap.queryTable("user")
.setFields("id", "name", "age")
.maps();
// 排除某些字段
List<Map<String, Object>> users = bootstrap.queryTable("user")
.setExceptFields("password", "secret")
.maps();
Q13: 如何处理查询结果为空?
A:
// 方式一:使用 Optional
Optional<User> user = bootstrap.queryTable("user")
.addCondition(C.eq("id", 1))
.unique(User.class);
if (user.isPresent()) {
User u = user.get();
// 处理用户
} else {
// 处理为空情况
}
// 方式二:使用 orElse
User user = bootstrap.queryTable("user")
.addCondition(C.eq("id", 1))
.unique(User.class)
.orElse(null);
Q14: 条件值为 null 怎么办?
A:
// 方式一:动态添加条件
bootstrap.queryTable("user").addCondition(conds -> {
if (name != null) {
conds.add(C.like("name", name));
}
if (age != null) {
conds.add(C.eq("age", age));
}
}).list();
// 方式二:使用 enable
bootstrap.queryTable("user")
.addCondition(C.like("name", name).enable(name != null))
.addCondition(C.eq("age", age).enable(age != null))
.list();
Q15: 如何排序?
A:
// 单字段排序
List<User> users = bootstrap.queryTable("user")
.sort(Sorts.DESC, "id")
.list(User.class);
// 多字段排序
List<User> users = bootstrap.queryTable("user")
.sort(Sorts.DESC, "age")
.sort(Sorts.ASC, "id")
.list(User.class);
事务相关
Q16: 如何开启事务?
A: 三种方式:
方式一:JDBC 事务
import online.sanen.unabo.api.structure.enums.TransactionFactoryEnum;
Bootstrap bootstrap = Unabo.load(configuration -> {
// 配置...
configuration.setTransactionFactory(TransactionFactoryEnum.JdbcTransactionFactory);
});
// 使用事务
Unabo.openSession(() -> {
bootstrap.query(user).insert();
bootstrap.query(order).insert();
}, bootstrap);
方式二:Spring 事务
# application.yml
unabo:
sql-instances:
- id: sys
transaction: SpringManagedTransactionFactory
import org.springframework.transaction.annotation.Transactional;
@Transactional
public void method() {
// 数据库操作
}
方式三:MongoDB 事务
# application.yml
unabo:
nosql-instances:
- id: mongodb
transaction-factory: MongoDBTransactionFactory
Q17: Spring 事务和多数据源如何处理?
A: Spring 事务只支持单数据源回滚,多数据源场景必须使用 JDBC 事务:
// ✅ 正确:使用 JDBC 事务
Unabo.openSession(() -> {
bootstrap1.query(user).insert();
bootstrap2.query(order).insert();
}, bootstrap1, bootstrap2);
// ❌ 错误:Spring 事务无法保证多数据源同时回滚
@Transactional
public void method() {
bootstrap1.query(user).insert();
bootstrap2.query(order).insert();
}
Q18: 如何手动回滚事务?
A:
try {
bootstrap.openSession();
// 数据库操作
bootstrap.query(user).insert();
// 手动回滚
if (someCondition) {
bootstrap.rollback();
return;
}
// 提交
bootstrap.commit();
} catch (Exception e) {
try {
bootstrap.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
性能相关
Q19: 如何提升查询性能?
A:
只查询需要的字段
bootstrap.queryTable("user") .setFields("id", "name") .maps();使用索引字段作为查询条件
避免 N+1 查询
// ❌ 不好 for (Order order : orders) { User user = bootstrap.queryTable("user") .addCondition(C.eq("id", order.getUserId())) .unique() .orElse(null); } // ✅ 好 Set<Integer> userIds = orders.stream() .map(Order::getUserId) .collect(Collectors.toSet()); List<User> users = bootstrap.queryTable("user") .addCondition(C.in("id", userIds)) .list();使用批量操作
// 批量插入 bootstrap.query(users).insert();合理使用分页
bootstrap.queryTable("user") .limit(0, 100) .list();
Q20: 如何使用批量操作?
A:
// 批量插入
List<User> users = Arrays.asList(user1, user2, user3);
bootstrap.query(users).insert();
// 批量更新
bootstrap.query(users).update();
// 分批处理大数量
int batchSize = 500;
for (int i = 0; i < allUsers.size(); i += batchSize) {
int end = Math.min(i + batchSize, allUsers.size());
List<User> batch = allUsers.subList(i, end);
bootstrap.query(batch).insert();
}
Q21: 如何开启缓存?
A:
WARNING
缓存默认关闭,建议只在只读数据上使用,否则可能导致脏数据。
Bootstrap bootstrap = Unabo.load(configuration -> {
configuration.setUrl("jdbc:mysql://localhost:3306/test");
configuration.setCache(true); // 开启缓存
});
// 或只缓存特定表
bootstrap.queryTable("sys_config")
.enableCache(true)
.maps();
Q22: 如何使用流式处理大数据?
A:
使用 stream() 方法分批处理大数据,避免一次性加载到内存:
// 方式1:按批次处理
bootstrap.queryTable("user").stream(1000, rows -> {
// 每批最多 1000 条数据
for (Map<String, Object> row : rows) {
User user = new User();
user.setId((Integer) row.get("id"));
user.setName((String) row.get("name"));
processUser(user);
}
});
// 方式2:逐行处理 + 批次回调
bootstrap.queryTable("user").stream(1000,
row -> {
// 逐行处理
User user = new User();
user.setId((Integer) row.get("id"));
user.setName((String) row.get("name"));
processUser(user);
},
rows -> {
// 每批处理完成后的回调
System.out.println("完成一批: " + rows.size() + " 条");
}
);
参数说明:
bufferSize: 批次大小,每批处理的记录数rowProcess: 逐行处理回调(可选)consumer: 批次处理回调
Spring Boot 集成
Q23: 如何在 Spring Boot 中使用 Unabo?
A:
步骤1:添加依赖
<dependency>
<groupId>online.sanen</groupId>
<artifactId>unabo</artifactId>
<version>1.2.4</version>
</dependency>
步骤2:配置 application.yml
unabo:
enable: true
sql-instances:
- id: sys
driver-option: mysql-cj
datasource-type: hikaricp
url: jdbc:mysql://localhost:3306/test
username: root
password: 123456
步骤3:排除默认数据源
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
步骤4:注入 Bootstrap
import javax.annotation.Resource;
@Service
public class UserService {
@Resource
private Bootstrap bootstrap;
public List<User> getAllUsers() {
return bootstrap.queryTable("user").list(User.class);
}
}
Q24: 多数据源如何配置?
A:
unabo:
enable: true
sql-instances:
- id: db1
url: jdbc:mysql://localhost:3306/db1
username: root
password: 123456
- id: db2
url: jdbc:mysql://localhost:3306/db2
username: root
password: 123456
@Service
public class MultiDataSourceService {
@Resource
private Bootstrap db1; // Bean 名为配置的 id
@Resource
private Bootstrap db2;
public void method() {
db1.queryTable("user").list();
db2.queryTable("order").list();
}
}
Q25: Spring 事务不生效怎么办?
A: 检查以下几点:
- 是否配置了 Spring 事务工厂
unabo:
sql-instances:
- id: sys
transaction: SpringManagedTransactionFactory # 必须配置
- 是否启用了事务管理
@EnableTransactionManagement
public class Application {
// ...
}
- 是否排除了默认数据源
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class Application {
// ...
}
- 是否添加了 @Transactional 注解
@Transactional
public void method() {
// 数据库操作
}
- 是否在同一个类中调用
// ❌ 不会生效(内部调用)
public void method1() {
method2();
}
@Transactional
public void method2() {
// 事务代码
}
// ✅ 会生效(外部调用)
@Service
public class ServiceA {
@Resource
private ServiceB serviceB;
public void method1() {
serviceB.method2();
}
}
@Service
public class ServiceB {
@Transactional
public void method2() {
// 事务代码
}
}
MongoDB 相关
Q26: 如何使用 MongoDB?
A:
import online.sanen.unabo.nosql.Bootstrap;
// 创建 MongoDB 实例
Bootstrap bootstrap = Unabo.nosql.load(configurationNosql -> {
configurationNosql.setIp("127.0.0.1");
configurationNosql.setPort(27017);
configurationNosql.setSchema("test");
configurationNosql.setUsername("root");
configurationNosql.setPassword("123456");
});
// 插入文档
Map<String, Object> doc = new HashMap<>();
doc.put("name", "张三");
doc.put("age", 25);
bootstrap.queryMap("user", doc).insert();
// 查询文档
List<Map<String, Object>> users = bootstrap.queryTable("user").maps();
Q27: MongoDB 如何使用聚合管道?
A:
// Unwind 展开
bootstrap.pipeline("user")
.unwind("child")
.project("id", "name", "age", "child.name", "child.age")
.alias(Collections.asMap("child.name", "cName", "child.age", "cAge"))
.addCondition(conds -> conds.add(gte("age", 18)))
.maps();
// Group 分组
bootstrap.pipeline("group")
.group(Collections.singletonList("name"),
Aggregation.newInstance().avg("score").first("sex"))
.project("score_avg", "sex_first", "name")
.alias(Collections.asMap("score_avg", "score", "sex_first", "sex"))
.maps();
Q28: MongoDB 事务如何配置?
A:
unabo:
enable: true
nosql-instances:
- id: mongodb
ip: 127.0.0.1
port: 27017
username: root
password: 123456
schema: test
transaction-factory: MongoDBTransactionFactory
WARNING
MongoDB 事务需要副本集配置,开发环境至少需要单副本节点。
错误排查
Q29: 连接数据库失败怎么办?
A: 检查以下几点:
- URL 是否正确
// MySQL
configuration.setUrl("jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC");
// PostgreSQL
configuration.setUrl("jdbc:postgresql://localhost:5432/test");
// Oracle
configuration.setUrl("jdbc:oracle:thin:@//localhost:1521/ORCL");
用户名和密码是否正确
数据库服务是否启动
防火墙是否开放端口
数据库驱动是否正确
configuration.setDriverOption(DriverOption.MYSQL_CJ);
Q30: 表不存在怎么办?
A:
// 检查表是否存在
if (bootstrap.dataInformation().containsTable("user")) {
// 表存在,执行查询
} else {
// 表不存在,创建表
Map<String, Object> schema = new HashMap<>();
schema.put("id", 1);
schema.put("name", "");
bootstrap.queryMap("user", schema).setPrimary("id").create();
}
Q31: 字段类型不匹配怎么办?
A:
// 方式一:关闭自动类型转换
bootstrap.queryTable("user")
.addCondition(C.eq("age", "18").valueCastType(false))
.list();
// 方式二:使用正确的类型
bootstrap.queryTable("user")
.addCondition(C.eq("age", 18)) // Integer 类型
.list();
Q32: 如何查看执行的 SQL?
A:
// 方式一:开启 SQL 日志
configuration.setIsShowSql(true);
// 方式二:格式化 SQL
configuration.setFormat(true);
Q33: 批量操作失败怎么办?
A: 检查以下几点:
- 数据是否正确
// 检查数据
users.forEach(user -> {
if (user.getName() == null) {
throw new RuntimeException("用户名不能为空");
}
});
字段类型是否匹配
主键是否重复
批量大小是否合理
// 分批处理
int batchSize = 500;
for (int i = 0; i < allUsers.size(); i += batchSize) {
int end = Math.min(i + batchSize, allUsers.size());
List<User> batch = allUsers.subList(i, end);
bootstrap.query(batch).insert();
}
Q34: 如何处理并发问题?
A:
// 方式一:使用乐观锁
@Table(name = "user")
public class User {
@Id
private Integer id;
private String name;
@Column(name = "version")
private Integer version; // 版本号
}
// 更新时检查版本
bootstrap.queryTable("user")
.addCondition(C.eq("id", userId))
.addCondition(C.eq("version", oldVersion))
.update(Collections.singletonMap("version", oldVersion + 1));
// 方式二:使用数据库事务
Unabo.openSession(() -> {
// 事务操作
}, bootstrap);
Q35: 内存溢出怎么办?
A:
// ❌ 不好:一次性加载所有数据
List<User> allUsers = bootstrap.queryTable("user").list();
// ✅ 好:使用分页
Page page = new Page(1, 100);
page.setCount(bootstrap.queryTable("user").count());
List<User> users = bootstrap.queryTable("user")
.limit(page.skip(), 100)
.list();
// 或使用流式处理
bootstrap.queryTable("user").stream(100, rows -> {
for (Map<String, Object> row : rows) {
User user = new User();
// 处理用户
}
});
其他问题
Q36: 如何获取帮助?
A:
- 📧 邮箱:282854237@qq.com
- 🌐 官网:http://unabo.sanen.online
- 🐛 GitHub Issues:https://github.com/sanen-projects/unabo/issues
Q37: Unabo 和 MyBatis/JPA 有什么区别?
A:
| 特性 | Unabo | MyBatis | JPA |
|---|---|---|---|
| 学习曲线 | 简单 | 中等 | 复杂 |
| SQL 灵活性 | 高 | 很高 | 低 |
| 性能 | 好 | 好 | 一般 |
| Spring 集成 | 好 | 很好 | 很好 |
| 代码量 | 少 | 中等 | 多 |
| 社区支持 | 小 | 大 | 大 |
| MongoDB 支持 | 原生 | 需要额外配置 | 需要 Spring Data MongoDB |
Q38: Unabo 适合什么场景?
A:
推荐使用:
- 中小型项目
- 需要 SQL + MongoDB 混合使用
- 快速原型开发
- 学习和研究 ORM 原理
- 对性能要求不是极端的场景
不推荐使用:
- 大型企业级应用
- 需要复杂关联查询的场景
- 要求完善的监控和运维支持
- 团队技术栈需要标准化(如 MyBatis、JPA)
Q39: 如何升级 Unabo 版本?
A:
- 修改 pom.xml 中的版本号
<dependency>
<groupId>online.sanen</groupId>
<artifactId>unabo</artifactId>
<version>最新版本</version>
</dependency>
查看 更新日志 了解变更
测试兼容性
部署上线
Q40: 如何参与贡献?
A:
欢迎贡献!请按照以下步骤:
- Fork 项目仓库
- 创建特性分支
- 提交更改
- 推送到分支
- 创建 Pull Request
获取更多帮助
如果以上 FAQ 无法解决你的问题,请:
- 查看官方文档:http://unabo.sanen.online
- 查看更新日志:更新日志
- 提交 Issue:https://github.com/sanen-projects/unabo/issues
- 联系作者:282854237@qq.com