Appearance
数据库操作参考
💾 数据库操作:MyBatis-Flex使用指南和最佳实践
📋 目录
MyBatis-Flex基础
核心依赖
xml
<dependency>
<groupId>com.mybatis-flex</groupId>
<artifactId>mybatis-flex-spring-boot-starter</artifactId>
<version>1.10.8</version>
</dependency>
基础配置
yaml
# application.yml
mybatis-flex:
configuration:
map-underscore-to-camel-case: false # 不自动转换下划线
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # SQL日志
global-config:
print-banner: true
基本使用流程
java
// 1. 实体类继承BaseEntity
@Table("business_table")
public class BusinessEntity extends BaseEntity {
// 字段定义...
}
// 2. Mapper接口继承BaseMapper
@Mapper
public interface BusinessMapper extends BaseMapper<BusinessEntity> {
// 自动获得基础CRUD方法
}
// 3. Service使用
@Service
@UseDataSource(DbNameConst.JPWISE_DEMO)
public class BusinessServiceImpl extends ServiceImpl<BusinessMapper, BusinessEntity> {
// 业务逻辑实现
}
实体类映射
实体类注解
java
@Data
@Table("BUSINESS_TABLE") // 表名映射
@Schema(description = "业务实体")
public class BusinessEntity extends BaseEntity {
// 主键映射
@Id(value = "ID")
@Column(value = "ID")
@JsonProperty("ID")
private String ID;
// 普通字段映射
@Schema(description = "业务标题")
@Column("TITLE") // 数据库字段名
@JsonProperty("TITLE") // JSON序列化字段名
private String TITLE;
// 忽略字段(不参与数据库操作)
@Column(ignore = true)
private String tempField;
// 只插入字段
@Column(onInsertValue = "now()")
private Date createTime;
// 只更新字段
@Column(onUpdateValue = "now()")
private Date updateTime;
}
字段类型映射
java
// 数值类型
private Integer intField; // INT
private Long longField; // BIGINT
private BigDecimal decimalField; // DECIMAL
// 字符串类型
private String stringField; // VARCHAR
// 日期时间类型
private Date dateField; // DATETIME
// 布尔类型
private Boolean boolField; // TINYINT(1)
// JSON类型(MySQL 5.7+)
private String jsonField; // JSON
查询构建器
QueryWrapper基础用法
java
QueryWrapper queryWrapper = new QueryWrapper();
// 等于条件
queryWrapper.eq(BusinessEntity::getTITLE, "测试标题");
// 不等于条件
queryWrapper.ne(BusinessEntity::getSTATUS, "DELETED");
// 模糊查询
queryWrapper.like(BusinessEntity::getNAME, "关键字");
// 范围查询
queryWrapper.between(BusinessEntity::getAMOUNT, 100, 1000);
queryWrapper.ge(BusinessEntity::getCREATORTIME, startDate); // 大于等于
queryWrapper.le(BusinessEntity::getCREATORTIME, endDate); // 小于等于
// 空值判断
queryWrapper.isNull(BusinessEntity::getDELETEUSER); // 为空
queryWrapper.isNotNull(BusinessEntity::getDELETEUSER); // 不为空
// IN查询
queryWrapper.in(BusinessEntity::getSTATUS, Arrays.asList("PENDING", "APPROVED"));
// 排序
queryWrapper.orderBy(BusinessEntity::getCREATORTIME, false); // false = DESC
queryWrapper.orderBy(BusinessEntity::getTITLE, true); // true = ASC
复杂条件组合
java
// AND条件组合
queryWrapper.eq(BusinessEntity::getSTATUS, "ACTIVE")
.like(BusinessEntity::getTITLE, "关键字")
.ge(BusinessEntity::getAMOUNT, 100);
// OR条件组合(需要显式类型转换)
import java.util.function.Consumer;
queryWrapper.and((Consumer<QueryWrapper>) w -> {
w.like(BusinessEntity::getTITLE, keyword)
.or((Consumer<QueryWrapper>) ww -> ww.like(BusinessEntity::getCODE, keyword))
.or((Consumer<QueryWrapper>) ww -> ww.like(BusinessEntity::getNAME, keyword));
});
// 复杂嵌套条件
queryWrapper.eq(BusinessEntity::getSTATUS, "ACTIVE")
.and(w -> w.ge(BusinessEntity::getAMOUNT, 100)
.le(BusinessEntity::getAMOUNT, 1000))
.or(w -> w.eq(BusinessEntity::getUSERTYPE, "VIP"));
子查询
java
// EXISTS子查询
queryWrapper.exists("SELECT 1 FROM detail_table WHERE detail_table.main_id = " +
BusinessEntity.TABLE + ".ID");
// IN子查询
queryWrapper.inSql(BusinessEntity::getDEPARTMENTID,
"SELECT id FROM department WHERE type = 'SALES'");
// NOT IN子查询
queryWrapper.notInSql(BusinessEntity::getUserId,
"SELECT user_id FROM blacklist");
分页查询
基础分页
java
@Override
public List<BusinessEntity> getList(QueryBuilder pagination) {
QueryWrapper queryWrapper = new QueryWrapper();
// 构建查询条件
queryWrapper.eq(BusinessEntity::getENABLEDMARK, "1")
.isNull(BusinessEntity::getDELETEUSER);
// 创建分页对象
Page<BusinessEntity> page = new Page<>(
pagination.getCurrentPage(), // 当前页,从1开始
pagination.getPageSize() // 页大小
);
// 执行分页查询
Page<BusinessEntity> result = this.page(page, queryWrapper);
// 设置分页数据
return pagination.setData(result.getRecords(), result.getTotalRow());
}
分页优化
java
// 大数据量分页优化(避免深分页)
Page<BusinessEntity> page = new Page<>(
pagination.getCurrentPage(),
pagination.getPageSize(),
false // 不查询总数,提高性能
);
// 使用JdbcUtil进行分页查询(复杂SQL场景)
public JdbcPageMod<Map<String, Object>> getPageDataWithJdbc(QueryBuilder queryBuilder) {
Connection conn = ConnUtil.getConn(DbNameConst.JPWISE_DEMO);
String sql = """
SELECT * FROM business_table
WHERE status = ?
AND enabled_mark = '1'
AND delete_user IS NULL
""";
PrepareSqlDTO sqlDTO = new PrepareSqlDTO();
sqlDTO.setConn(conn);
sqlDTO.setPrepareSql(sql);
sqlDTO.setDataList(Arrays.asList("ACTIVE"));
return JdbcUtil.queryPage(sqlDTO, queryBuilder);
}
多数据源配置
数据源常量
java
public class DbNameConst {
public static final String JPWISE_BASE = "jpwise_base"; // 基础数据库
public static final String JPWISE_DEMO = "jpwise_demo"; // 测试项目
public static final String JPWISE_HR = "jpwise_hr"; // 人力资源
public static final String JPWISE_AFFAIR = "jpwise_affair"; // 事务数据库
public static final String JPWISE_DESIGN = "jpwise_design"; // 设计项目
public static final String JPWISE_DOCSYSTEM = "jpwise_docsystem"; // 档案系统
}
Service层数据源指定
java
// 在Service实现类上指定数据源
@Service
@UseDataSource(DbNameConst.JPWISE_DEMO) // 使用测试数据库
public class BusinessServiceImpl extends ServiceImpl<BusinessMapper, BusinessEntity> {
// 方法级别数据源切换(如果需要)
@UseDataSource(DbNameConst.JPWISE_BASE)
public List<SystemEntity> getSystemData() {
// 这个方法使用基础数据库
}
}
跨数据源查询
java
@Service
public class CrossDataSourceService {
@Autowired
private BusinessMapper businessMapper;
@Autowired
private SystemMapper systemMapper;
public void crossDataSourceOperation() {
// 查询业务数据(JPWISE_DEMO)
List<BusinessEntity> businessList = businessMapper.selectAll();
// 查询系统数据(JPWISE_BASE)
List<SystemEntity> systemList = systemMapper.selectAll();
// 注意:跨数据源无法使用数据库事务,需要应用层控制
}
}
事务管理
基础事务
java
@Service
@UseDataSource(DbNameConst.JPWISE_DEMO)
public class BusinessServiceImpl {
@Transactional(rollbackFor = Exception.class)
public void createBusiness(BusinessEntity entity) {
// 保存主表
this.save(entity);
// 保存子表
detailService.saveBatch(entity.getDetailList());
// 如果出现异常,所有操作都会回滚
if (someCondition) {
throw new BusinessException("业务异常,触发回滚");
}
}
}
事务传播行为
java
// 必须在事务中执行
@Transactional(propagation = Propagation.REQUIRED)
public void requiredTransaction() { }
// 创建新事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void newTransaction() { }
// 非事务执行
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void nonTransaction() { }
// 只读事务(优化性能)
@Transactional(readOnly = true)
public List<BusinessEntity> readOnlyQuery() { }
手动事务控制
java
@Service
public class ManualTransactionService {
@Autowired
private DataSourceTransactionManager transactionManager;
public void manualTransaction() {
TransactionStatus status = transactionManager.getTransaction(
new DefaultTransactionDefinition());
try {
// 业务操作
businessOperation();
// 提交事务
transactionManager.commit(status);
} catch (Exception e) {
// 回滚事务
transactionManager.rollback(status);
throw e;
}
}
}
性能优化
查询优化
java
// 1. 只查询需要的字段
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.select(BusinessEntity::getID, BusinessEntity::getTITLE, BusinessEntity::getSTATUS)
.eq(BusinessEntity::getENABLEDMARK, "1");
// 2. 使用索引字段作为查询条件
queryWrapper.eq(BusinessEntity::getID, id) // 主键查询,最快
.eq(BusinessEntity::getCODE, code); // 如果CODE字段有索引
// 3. 避免函数操作
// ❌ 错误:在字段上使用函数
queryWrapper.apply("DATE(create_time) = '2024-08-14'");
// ✅ 正确:使用范围查询
queryWrapper.ge(BusinessEntity::getCREATORTIME, startOfDay)
.lt(BusinessEntity::getCREATORTIME, endOfDay);
批量操作优化
java
// 批量插入
@Override
public void batchInsert(List<BusinessEntity> entities) {
// 分批处理,避免内存溢出
int batchSize = 1000;
for (int i = 0; i < entities.size(); i += batchSize) {
int end = Math.min(i + batchSize, entities.size());
List<BusinessEntity> batch = entities.subList(i, end);
this.saveBatch(batch);
}
}
// 批量更新
@Override
public void batchUpdate(List<BusinessEntity> entities) {
this.updateBatchById(entities, entities.size());
}
缓存优化
java
@Service
public class CachedBusinessService {
@Autowired
private RedisUtil redisUtil;
public BusinessEntity getWithCache(String id) {
String cacheKey = "business:" + id;
// 先查缓存
BusinessEntity cached = (BusinessEntity) redisUtil.getString(cacheKey);
if (cached != null) {
return cached;
}
// 查数据库
BusinessEntity entity = this.getById(id);
if (entity != null) {
// 存入缓存
redisUtil.insert(cacheKey, entity, RedisUtil.CACHE_HOUR);
}
return entity;
}
}
复杂SQL操作
使用JdbcUtil处理复杂SQL
对于复杂的SQL查询、动态条件查询、批量操作等,推荐在Service层使用JdbcUtil:
java
import jpwise.database.util.JdbcUtil;
import jpwise.database.util.ConnUtil;
import jpwise.database.constant.DbNameConst;
import jpwise.database.model.dto.PrepareSqlDTO;
import jpwise.database.model.page.JdbcPageMod;
@Service
@UseDataSource(DbNameConst.JPWISE_DEMO)
public class BusinessServiceImpl extends ServiceImpl<BusinessMapper, BusinessEntity> {
// 复杂关联查询
public List<Map<String, Object>> getDetailWithRelations(String status) {
Connection conn = ConnUtil.getConn(DbNameConst.JPWISE_DEMO);
String sql = """
SELECT
b.*,
u.real_name as creator_name,
d.name as department_name
FROM business_table b
LEFT JOIN sys_user u ON b.creator_user = u.id
LEFT JOIN sys_department d ON b.organize = d.id
WHERE b.enabled_mark = '1'
AND b.delete_user IS NULL
AND b.status = ?
ORDER BY b.creator_time DESC
""";
PrepareSqlDTO sqlDTO = new PrepareSqlDTO();
sqlDTO.setConn(conn);
sqlDTO.setPrepareSql(sql);
sqlDTO.setDataList(Arrays.asList(status));
return JdbcUtil.queryList(sqlDTO);
}
// 动态条件查询
public List<Map<String, Object>> dynamicQuery(String status, String keyword,
Date startDate, Date endDate) {
Connection conn = ConnUtil.getConn(DbNameConst.JPWISE_DEMO);
StringBuilder sql = new StringBuilder("""
SELECT * FROM business_table
WHERE enabled_mark = '1' AND delete_user IS NULL
""");
List<Object> params = new ArrayList<>();
if (StringUtil.isNotEmpty(status)) {
sql.append(" AND status = ?");
params.add(status);
}
if (StringUtil.isNotEmpty(keyword)) {
sql.append(" AND (title LIKE ? OR code LIKE ?)");
params.add("%" + keyword + "%");
params.add("%" + keyword + "%");
}
if (startDate != null) {
sql.append(" AND creator_time >= ?");
params.add(startDate);
}
if (endDate != null) {
sql.append(" AND creator_time <= ?");
params.add(endDate);
}
sql.append(" ORDER BY creator_time DESC");
PrepareSqlDTO sqlDTO = new PrepareSqlDTO();
sqlDTO.setConn(conn);
sqlDTO.setPrepareSql(sql.toString());
sqlDTO.setDataList(params);
return JdbcUtil.queryList(sqlDTO);
}
// 批量更新
@Transactional(rollbackFor = Exception.class)
public int batchUpdateStatus(List<String> ids, String status) {
Connection conn = ConnUtil.getConn(DbNameConst.JPWISE_DEMO);
String inClause = ids.stream().map(id -> "?").collect(Collectors.joining(","));
String sql = String.format("""
UPDATE business_table
SET status = ?,
last_modify_time = NOW()
WHERE id IN (%s)
""", inClause);
List<Object> params = new ArrayList<>();
params.add(status);
params.addAll(ids);
PrepareSqlDTO sqlDTO = new PrepareSqlDTO();
sqlDTO.setConn(conn);
sqlDTO.setPrepareSql(sql);
sqlDTO.setDataList(params);
return JdbcUtil.executeUpdate(sqlDTO);
}
// 统计查询
public Map<String, Object> getStatistics() {
Connection conn = ConnUtil.getConn(DbNameConst.JPWISE_DEMO);
String sql = """
SELECT
COUNT(*) as total,
SUM(CASE WHEN status = 'ACTIVE' THEN 1 ELSE 0 END) as active_count,
SUM(CASE WHEN status = 'PENDING' THEN 1 ELSE 0 END) as pending_count,
SUM(amount) as total_amount
FROM business_table
WHERE enabled_mark = '1' AND delete_user IS NULL
""";
PrepareSqlDTO sqlDTO = new PrepareSqlDTO();
sqlDTO.setConn(conn);
sqlDTO.setPrepareSql(sql);
sqlDTO.setDataList(new ArrayList<>());
List<Map<String, Object>> result = JdbcUtil.queryList(sqlDTO);
return result.isEmpty() ? new HashMap<>() : result.get(0);
}
}
调试和监控
SQL日志配置
yaml
# application.yml
mybatis-flex:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 或使用Logback配置
logging:
level:
jpwise.base.mapper: DEBUG # 输出SQL日志
💡 提示:MyBatis-Flex提供了强大而灵活的数据库操作能力。合理使用QueryWrapper可以构建复杂查询,配合分页和缓存机制可以显著提升性能。记住在Service层指定正确的数据源,并合理使用事务管理。