Appearance
纯手工开发方式完整指南
⚙️ 精细控制:适用于复杂业务逻辑、性能敏感场景、特殊接口设计
📋 目录
核心概念
什么是纯手工开发?
纯手工开发方式是完全自定义实现Controller、Service、Mapper的开发模式,提供最大的灵活性和控制力。
适用场景
- 🔧 复杂业务逻辑 - 需要精细控制每个步骤
- ⚡ 性能优化 - 需要特殊的查询优化
- 🎯 特殊接口设计 - 非标准CRUD操作
- 🔌 第三方集成 - 需要调用外部API
- 📊 复杂查询 - 多表关联、统计分析
纯手工开发强制规范
选择纯手工开发后,必须遵循以下规范:
- 不继承Basic系列基类 - 完全自定义实现
- 使用ServiceImpl继承MyBatis-Flex的ServiceImpl - 获得基础的CRUD能力
- 手动实现所有CRUD方法 - 根据业务需求自定义实现
- 严格遵循项目的编码规范 - 包括异常处理、日志记录、返回格式等
核心依赖
java
// MyBatis-Flex
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.annotation.UseDataSource;
// Spring
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
// 项目基础
import jpwise.base.ActionResult;
import jpwise.annotation.BuildQuery;
import jpwise.base.query.mode.QueryBuilder;
基础架构
三层架构标准
Controller (接口层)
↓ 调用
Service (业务层)
↓ 调用
Mapper (数据层)
包结构规范
jpwise/extend/
├── controller/ # REST API控制器
├── service/ # 业务逻辑接口
├── service/impl/ # 业务逻辑实现
├── base/
│ ├── entity/ # 实体类
│ └── mapper/ # Mapper接口
└── model/ # DTO/VO对象(可选)
Controller层开发
基础Controller结构
java
@RestController
@RequestMapping("/api/extend/Business")
public class BusinessController {
@Autowired
private BusinessService businessService;
// 分页查询列表
@BuildQuery // 注解在方法上,不是参数上
@GetMapping
public ActionResult<PageListVO<BusinessEntity>> getList(QueryBuilder pagination) {
List<BusinessEntity> data = businessService.getList(pagination);
PaginationVO paginationVO = JsonUtil.getJsonToBean(pagination, PaginationVO.class);
return ActionResult.page(data, paginationVO);
}
// 获取详情
@GetMapping("/{id}")
public ActionResult<BusinessEntity> getInfo(@PathVariable String id) {
BusinessEntity entity = businessService.getInfo(id);
if (entity == null) {
return ActionResult.fail(MsgCode.FA002.get());
}
return ActionResult.success(entity);
}
// 创建数据
@PostMapping
public ActionResult<String> create(@Valid @RequestBody BusinessEntity entity) {
businessService.create(entity);
return ActionResult.success(MsgCode.SU001.get());
}
// 更新数据(使用POST,不用PUT)
@PostMapping("/update/{id}")
public ActionResult<String> update(@PathVariable String id,
@Valid @RequestBody BusinessEntity entity) {
businessService.update(id, entity);
return ActionResult.success(MsgCode.SU001.get());
}
// 删除数据(使用POST,不用DELETE)
@PostMapping("/{id}")
public ActionResult<String> delete(@PathVariable String id) {
businessService.delete(id);
return ActionResult.success(MsgCode.SU001.get());
}
}
复杂查询接口
java
@RestController
@RequestMapping("/api/extend/Statistics")
public class StatisticsController {
@Autowired
private StatisticsService statisticsService;
// 统计分析接口
@BuildQuery
@GetMapping("/summary")
public ActionResult<Map<String, Object>> getSummary(QueryBuilder query) {
// 解析查询参数
String startDate = ConvertUtil.toStr(query.getCondition().get("startDate"));
String endDate = ConvertUtil.toStr(query.getCondition().get("endDate"));
String department = ConvertUtil.toStr(query.getCondition().get("department"));
Map<String, Object> result = statisticsService.getSummary(startDate, endDate, department);
return ActionResult.success(result);
}
// 导出Excel
@PostMapping("/export")
public void export(@RequestBody Map<String, Object> params, HttpServletResponse response) {
statisticsService.exportExcel(params, response);
}
// 批量操作
@PostMapping("/batch")
public ActionResult<String> batchOperation(@RequestBody BatchOperationDTO dto) {
statisticsService.batchProcess(dto);
return ActionResult.success("批量操作成功");
}
}
Service层开发
Service接口定义
java
public interface BusinessService {
// 基础CRUD
List<BusinessEntity> getList(QueryBuilder pagination);
BusinessEntity getInfo(String id);
void create(BusinessEntity entity);
void update(String id, BusinessEntity entity);
void delete(String id);
// 业务方法
void approve(String id, ApprovalDTO approval);
List<BusinessEntity> getMyTasks(String userId);
Map<String, Object> getStatistics(StatisticsQuery query);
}
Service实现
java
import lombok.extern.slf4j.Slf4j;
@Slf4j // 必须添加日志注解
@Service
@UseDataSource(DbNameConst.JPWISE_DEMO) // 必须指定数据源
public class BusinessServiceImpl extends ServiceImpl<BusinessMapper, BusinessEntity>
implements BusinessService {
@Autowired
private UserProvider userProvider;
@Autowired
private RedisUtil redisUtil;
@Autowired
private MessageService messageService;
@Override
public List<BusinessEntity> getList(QueryBuilder pagination) {
// 构建查询条件
QueryWrapper queryWrapper = new QueryWrapper();
// 基础过滤
queryWrapper.eq(BusinessEntity::getENABLEDMARK, "1")
.isNull(BusinessEntity::getDELETEUSER);
// 关键字搜索(多字段OR)
if (StringUtil.isNotEmpty(pagination.getKeyword())) {
queryWrapper.and((Consumer<QueryWrapper>) w -> {
w.like(BusinessEntity::getTITLE, pagination.getKeyword())
.or((Consumer<QueryWrapper>) ww -> ww.like(BusinessEntity::getCODE, pagination.getKeyword()))
.or((Consumer<QueryWrapper>) ww -> ww.like(BusinessEntity::getNAME, pagination.getKeyword()));
});
}
// 条件过滤
Map<String, Object> condition = pagination.getCondition();
if (condition != null) {
// 日期范围
String startDate = ConvertUtil.toStr(condition.get("startDate"));
String endDate = ConvertUtil.toStr(condition.get("endDate"));
if (StringUtil.isNotEmpty(startDate) && StringUtil.isNotEmpty(endDate)) {
Date start = DateUtil.stringToDate(startDate + " 00:00:00");
Date end = DateUtil.stringToDate(endDate + " 23:59:59");
queryWrapper.between(BusinessEntity::getCREATORTIME, start, end);
}
// 状态过滤
String status = ConvertUtil.toStr(condition.get("status"));
if (StringUtil.isNotEmpty(status)) {
queryWrapper.eq(BusinessEntity::getSTATUS, status);
}
}
// 排序(false = DESC, true = ASC)
queryWrapper.orderBy(BusinessEntity::getCREATORTIME, false);
// 执行分页查询
Page<BusinessEntity> page = new Page<>(pagination.getCurrentPage(), pagination.getPageSize());
Page<BusinessEntity> result = this.page(page, queryWrapper);
// 设置分页数据
return pagination.setData(result.getRecords(), result.getTotalRow());
}
@Override
public BusinessEntity getInfo(String id) {
// 从缓存获取
String cacheKey = "business:info:" + id;
BusinessEntity cached = (BusinessEntity) redisUtil.getString(cacheKey);
if (cached != null) {
return cached;
}
// 从数据库查询
BusinessEntity entity = this.getById(id);
if (entity != null) {
// 加载关联数据
loadRelatedData(entity);
// 存入缓存
redisUtil.insert(cacheKey, entity, RedisUtil.CACHE_HOUR);
}
return entity;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void create(BusinessEntity entity) {
// 设置基础信息
UserInfo userInfo = userProvider.get();
entity.setID(RandomUtil.uuId());
entity.setCREATORUSER(userInfo.getUserId());
entity.setCREATORTIME(DateUtil.getNowDate());
entity.setENABLEDMARK("1");
entity.setORGANIZE(userInfo.getOrganizeId());
// 生成业务编号
if (StringUtil.isEmpty(entity.getCODE())) {
entity.setCODE(generateBusinessCode());
}
// 业务验证
validateBusiness(entity);
// 保存数据
this.save(entity);
// 发送通知
sendCreateNotification(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void update(String id, BusinessEntity entity) {
// 获取原数据
BusinessEntity original = this.getById(id);
if (original == null) {
throw new DataException("数据不存在");
}
// 权限验证
if (!hasUpdatePermission(original)) {
throw new BusinessException("无权限修改");
}
// 设置更新信息
entity.setID(id);
entity.setLASTMODIFYUSER(userProvider.get().getUserId());
entity.setLASTMODIFYTIME(DateUtil.getNowDate());
// 更新数据
this.updateById(entity);
// 清除缓存
redisUtil.remove("business:info:" + id);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void delete(String id) {
BusinessEntity entity = this.getById(id);
if (entity == null) {
throw new DataException("数据不存在");
}
// 软删除
entity.setDELETEUSER(userProvider.get().getUserId());
entity.setDELETETIME(DateUtil.getNowDate());
this.updateById(entity);
// 清除缓存
redisUtil.remove("business:info:" + id);
}
// 业务方法示例
@Override
@Transactional(rollbackFor = Exception.class)
public void approve(String id, ApprovalDTO approval) {
BusinessEntity entity = this.getById(id);
if (entity == null) {
throw new DataException("数据不存在");
}
// 更新状态
entity.setSTATUS("APPROVED");
entity.setAPPROVALTIME(DateUtil.getNowDate());
entity.setAPPROVALUSER(userProvider.get().getUserId());
entity.setAPPROVALREMARK(approval.getRemark());
this.updateById(entity);
// 发送通知
String title = "审批通过通知";
String content = "您的申请已审批通过";
messageService.sentMessage(Arrays.asList(entity.getCREATORUSER()), title, content, 1);
}
// 辅助方法
private String generateBusinessCode() {
String date = DateUtil.dateNow("yyyyMMdd");
String random = RandomUtil.enUuid().substring(0, 4).toUpperCase();
return "BUS" + date + random;
}
private void validateBusiness(BusinessEntity entity) {
// 业务验证逻辑
if (entity.getAMOUNT() != null && entity.getAMOUNT().compareTo(new BigDecimal("1000000")) > 0) {
throw new BusinessException("金额不能超过100万");
}
}
private boolean hasUpdatePermission(BusinessEntity entity) {
UserInfo user = userProvider.get();
// 创建者或管理员可以修改
return user.getUserId().equals(entity.getCREATORUSER()) || user.isAdmin();
}
private void loadRelatedData(BusinessEntity entity) {
// 加载关联数据的逻辑
}
private void sendCreateNotification(BusinessEntity entity) {
// 发送创建通知的逻辑
}
}
Mapper层开发
基础Mapper接口
java
import com.mybatisflex.core.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper // 必须添加@Mapper注解
public interface BusinessMapper extends BaseMapper<BusinessEntity> {
// 继承BaseMapper获得基础操作
// 可添加自定义SQL方法
}
Service实现中使用JdbcUtil处理复杂SQL
在Service实现类中处理复杂查询和数据库操作:
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;
import lombok.extern.slf4j.Slf4j;
@Slf4j // 必须添加日志注解
@Service
@UseDataSource(DbNameConst.JPWISE_DEMO)
public class BusinessServiceImpl extends ServiceImpl<BusinessMapper, BusinessEntity>
implements BusinessService {
@Autowired
private UserProvider userProvider;
// 复杂关联查询 - 在Service中使用JdbcUtil
public List<Map<String, Object>> getDetailWithRelations(String id) {
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.id = ?
""";
PrepareSqlDTO sqlDTO = new PrepareSqlDTO();
sqlDTO.setConn(conn);
sqlDTO.setPrepareSql(sql);
sqlDTO.setDataList(Arrays.asList(id));
return JdbcUtil.queryList(sqlDTO);
}
// 动态条件查询 - 在Service中使用JdbcUtil
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 1=1");
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);
}
// 分页查询 - 在Service中使用JdbcUtil
public JdbcPageMod<Map<String, Object>> getPageDataWithJdbc(QueryBuilder queryBuilder) {
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
""";
PrepareSqlDTO sqlDTO = new PrepareSqlDTO();
sqlDTO.setConn(conn);
sqlDTO.setPrepareSql(sql);
sqlDTO.setDataList(new ArrayList<>());
return JdbcUtil.queryPage(sqlDTO, queryBuilder);
}
// 统计查询 - 在Service中使用JdbcUtil
public List<Map<String, Object>> getMonthlyStatistics(Date startDate, Date endDate) {
Connection conn = ConnUtil.getConn(DbNameConst.JPWISE_DEMO);
String sql = """
SELECT
COUNT(*) as total,
SUM(amount) as totalAmount,
AVG(amount) as avgAmount,
DATE_FORMAT(creator_time, '%Y-%m') as month
FROM business_table
WHERE enabled_mark = '1'
AND delete_user IS NULL
AND creator_time BETWEEN ? AND ?
GROUP BY DATE_FORMAT(creator_time, '%Y-%m')
ORDER BY month DESC
""";
PrepareSqlDTO sqlDTO = new PrepareSqlDTO();
sqlDTO.setConn(conn);
sqlDTO.setPrepareSql(sql);
sqlDTO.setDataList(Arrays.asList(startDate, endDate));
return JdbcUtil.queryList(sqlDTO);
}
// 批量更新 - 在Service中使用JdbcUtil
@Transactional(rollbackFor = Exception.class)
public int batchUpdateStatus(List<String> ids, String status) {
Connection conn = ConnUtil.getConn(DbNameConst.JPWISE_DEMO);
// 构建IN子句
String inClause = ids.stream().map(id -> "?").collect(Collectors.joining(","));
String sql = String.format("""
UPDATE business_table
SET status = ?,
last_modify_user = ?,
last_modify_time = NOW()
WHERE id IN (%s)
""", inClause);
List<Object> params = new ArrayList<>();
params.add(status);
params.add(userProvider.get().getUserId());
params.addAll(ids);
PrepareSqlDTO sqlDTO = new PrepareSqlDTO();
sqlDTO.setConn(conn);
sqlDTO.setPrepareSql(sql);
sqlDTO.setDataList(params);
return JdbcUtil.executeUpdate(sqlDTO);
}
}
---
## 完整示例
### 费用报销功能实现
#### 1. 实体类
```java
@Data
@Table("EXPENSE_CLAIM")
@Schema(description = "费用报销")
public class ExpenseClaimEntity extends BaseEntity {
@Schema(description = "报销单号")
@Column("EXPENSENO")
@JsonProperty("EXPENSENO")
private String EXPENSENO;
@Schema(description = "报销标题")
@Column("TITLE")
@JsonProperty("TITLE")
private String TITLE;
@Schema(description = "报销金额")
@Column("AMOUNT")
@JsonProperty("AMOUNT")
private BigDecimal AMOUNT;
@Schema(description = "报销类型")
@Column("EXPENSETYPE")
@JsonProperty("EXPENSETYPE")
private String EXPENSETYPE;
@Schema(description = "审批状态")
@Column("APPROVALSTATUS")
@JsonProperty("APPROVALSTATUS")
private String APPROVALSTATUS;
}
2. Controller层
java
@RestController
@RequestMapping("/api/extend/ExpenseClaim")
public class ExpenseClaimController {
@Autowired
private ExpenseClaimService expenseClaimService;
@BuildQuery
@GetMapping
public ActionResult<PageListVO<ExpenseClaimEntity>> getList(QueryBuilder pagination) {
List<ExpenseClaimEntity> data = expenseClaimService.getList(pagination);
PaginationVO paginationVO = JsonUtil.getJsonToBean(pagination, PaginationVO.class);
return ActionResult.page(data, paginationVO);
}
@GetMapping("/{id}")
public ActionResult<ExpenseClaimEntity> getInfo(@PathVariable String id) {
ExpenseClaimEntity entity = expenseClaimService.getInfo(id);
return ActionResult.success(entity);
}
@PostMapping
public ActionResult<String> create(@Valid @RequestBody ExpenseClaimEntity entity) {
expenseClaimService.create(entity);
return ActionResult.success(MsgCode.SU001.get());
}
@PostMapping("/update/{id}")
public ActionResult<String> update(@PathVariable String id,
@Valid @RequestBody ExpenseClaimEntity entity) {
expenseClaimService.update(id, entity);
return ActionResult.success(MsgCode.SU001.get());
}
// 自定义业务接口
@PostMapping("/{id}/submit")
public ActionResult<String> submit(@PathVariable String id) {
expenseClaimService.submit(id);
return ActionResult.success("提交成功");
}
@GetMapping("/statistics")
public ActionResult<Map<String, Object>> getStatistics() {
Map<String, Object> stats = expenseClaimService.getUserStatistics();
return ActionResult.success(stats);
}
}
3. Service实现
java
import lombok.extern.slf4j.Slf4j;
@Slf4j // 必须添加日志注解
@Service
@UseDataSource(DbNameConst.JPWISE_DEMO)
public class ExpenseClaimServiceImpl extends ServiceImpl<ExpenseClaimMapper, ExpenseClaimEntity>
implements ExpenseClaimService {
@Autowired
private UserProvider userProvider;
@Autowired
private MessageService messageService;
@Override
public List<ExpenseClaimEntity> getList(QueryBuilder pagination) {
QueryWrapper queryWrapper = new QueryWrapper();
// 基础过滤
queryWrapper.eq(ExpenseClaimEntity::getENABLEDMARK, "1")
.isNull(ExpenseClaimEntity::getDELETEUSER);
// 只查看自己的报销单
UserInfo user = userProvider.get();
if (!user.isAdmin()) {
queryWrapper.eq(ExpenseClaimEntity::getCREATORUSER, user.getUserId());
}
// 关键字搜索
if (StringUtil.isNotEmpty(pagination.getKeyword())) {
queryWrapper.and((Consumer<QueryWrapper>) w -> {
w.like(ExpenseClaimEntity::getTITLE, pagination.getKeyword())
.or((Consumer<QueryWrapper>) ww -> ww.like(ExpenseClaimEntity::getEXPENSENO, pagination.getKeyword()));
});
}
// 状态过滤
Map<String, Object> condition = pagination.getCondition();
if (condition != null && condition.get("status") != null) {
queryWrapper.eq(ExpenseClaimEntity::getAPPROVALSTATUS, condition.get("status"));
}
queryWrapper.orderBy(ExpenseClaimEntity::getCREATORTIME, false);
Page<ExpenseClaimEntity> page = new Page<>(pagination.getCurrentPage(), pagination.getPageSize());
Page<ExpenseClaimEntity> result = this.page(page, queryWrapper);
return pagination.setData(result.getRecords(), result.getTotalRow());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void create(ExpenseClaimEntity entity) {
// 设置基础信息
entity.setID(RandomUtil.uuId());
entity.setCREATORUSER(userProvider.get().getUserId());
entity.setCREATORTIME(DateUtil.getNowDate());
entity.setENABLEDMARK("1");
entity.setAPPROVALSTATUS("DRAFT");
// 生成报销单号
if (StringUtil.isEmpty(entity.getEXPENSENO())) {
String date = DateUtil.dateNow("yyyyMMdd");
String random = RandomUtil.enUuid().substring(0, 4).toUpperCase();
entity.setEXPENSENO("EXP" + date + random);
}
// 验证金额
if (entity.getAMOUNT() == null || entity.getAMOUNT().compareTo(BigDecimal.ZERO) <= 0) {
throw new BusinessException("报销金额必须大于0");
}
this.save(entity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void submit(String id) {
ExpenseClaimEntity entity = this.getById(id);
if (entity == null) {
throw new DataException("报销单不存在");
}
if (!"DRAFT".equals(entity.getAPPROVALSTATUS())) {
throw new BusinessException("只能提交草稿状态的报销单");
}
// 更新状态
entity.setAPPROVALSTATUS("PENDING");
entity.setSUBMITTIME(DateUtil.getNowDate());
this.updateById(entity);
// 发送通知给审批人
List<String> approvers = getApprovers(entity);
String title = "报销审批提醒";
String content = String.format("您有新的报销单【%s】待审批,金额:%s元",
entity.getTITLE(), entity.getAMOUNT());
messageService.sentMessage(approvers, title, content, 1);
}
@Override
public Map<String, Object> getUserStatistics() {
String userId = userProvider.get().getUserId();
Map<String, Object> stats = new HashMap<>();
// 统计各状态数量
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq(ExpenseClaimEntity::getCREATORUSER, userId)
.eq(ExpenseClaimEntity::getENABLEDMARK, "1")
.isNull(ExpenseClaimEntity::getDELETEUSER);
List<ExpenseClaimEntity> all = this.list(queryWrapper);
long draftCount = all.stream().filter(e -> "DRAFT".equals(e.getAPPROVALSTATUS())).count();
long pendingCount = all.stream().filter(e -> "PENDING".equals(e.getAPPROVALSTATUS())).count();
long approvedCount = all.stream().filter(e -> "APPROVED".equals(e.getAPPROVALSTATUS())).count();
BigDecimal totalAmount = all.stream()
.filter(e -> "APPROVED".equals(e.getAPPROVALSTATUS()))
.map(ExpenseClaimEntity::getAMOUNT)
.reduce(BigDecimal.ZERO, BigDecimal::add);
stats.put("draftCount", draftCount);
stats.put("pendingCount", pendingCount);
stats.put("approvedCount", approvedCount);
stats.put("totalAmount", totalAmount);
return stats;
}
private List<String> getApprovers(ExpenseClaimEntity entity) {
// 获取审批人逻辑
return Arrays.asList("approver1", "approver2");
}
}
高级特性
1. 复杂查询优化
java
// 使用QueryWrapper的高级特性
QueryWrapper queryWrapper = new QueryWrapper();
// 子查询
queryWrapper.inSql(Entity::getDEPARTMENT,
"SELECT id FROM department WHERE type = 'SALES'");
// EXISTS查询
queryWrapper.exists("SELECT 1 FROM related_table WHERE related_id = " + Entity.TABLE + ".id");
// 自定义SQL片段
queryWrapper.apply("DATE_FORMAT(creator_time, '%Y-%m') = {0}", "2024-08");
// 分组和聚合
queryWrapper.select("department", "COUNT(*) as count", "SUM(amount) as total")
.groupBy(Entity::getDEPARTMENT)
.having("COUNT(*) > 10");
2. 事务管理
java
@Service
public class ComplexBusinessService {
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void complexOperation() {
// 多个数据库操作
mainTableService.save(mainEntity);
detailTableService.saveBatch(detailList);
// 如果出现异常,所有操作都会回滚
if (validateFails) {
throw new BusinessException("验证失败");
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void independentOperation() {
// 独立事务,不受外部事务影响
}
}
3. 批量操作优化
java
@Override
public void batchImport(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);
// 设置基础信息
batch.forEach(entity -> {
entity.setID(RandomUtil.uuId());
entity.setCREATORUSER(userProvider.get().getUserId());
entity.setCREATORTIME(DateUtil.getNowDate());
});
this.saveBatch(batch);
}
}
4. 缓存策略
java
@Service
public class CachedBusinessService {
@Autowired
private RedisUtil redisUtil;
public BusinessEntity getWithCache(String id) {
// 多级缓存策略
String l1Key = "business:l1:" + id;
String l2Key = "business:l2:" + id;
// L1缓存(短期)
Object l1Cache = redisUtil.getString(l1Key);
if (l1Cache != null) {
return (BusinessEntity) l1Cache;
}
// L2缓存(长期)
Object l2Cache = redisUtil.getString(l2Key);
if (l2Cache != null) {
redisUtil.insert(l1Key, l2Cache, RedisUtil.CACHE_MINUTE * 10);
return (BusinessEntity) l2Cache;
}
// 数据库查询
BusinessEntity entity = this.getById(id);
if (entity != null) {
redisUtil.insert(l1Key, entity, RedisUtil.CACHE_MINUTE * 10);
redisUtil.insert(l2Key, entity, RedisUtil.CACHE_DAY);
}
return entity;
}
}
最佳实践
1. 统一返回格式
java
// 始终使用ActionResult包装返回值
return ActionResult.success(data);
return ActionResult.fail(MsgCode.FA001.get());
return ActionResult.page(list, pagination);
2. 异常处理
java
// 使用项目定义的异常类型
throw new BusinessException("业务异常");
throw new DataException("数据异常");
// Service层捕获并转换异常
try {
// 业务逻辑
} catch (Exception e) {
log.error("操作失败", e);
throw new BusinessException("操作失败:" + e.getMessage());
}
3. 参数验证
java
// Controller层使用@Valid注解
@PostMapping
public ActionResult create(@Valid @RequestBody BusinessEntity entity) {
// @Valid会自动验证实体类的注解约束
}
// Service层手动验证
if (StringUtil.isEmpty(entity.getCODE())) {
throw new BusinessException("编号不能为空");
}
4. 日志记录
java
@Slf4j
@Service
public class BusinessServiceImpl {
public void businessMethod() {
log.info("开始执行业务方法,参数:{}", params);
try {
// 业务逻辑
log.debug("中间处理步骤");
} catch (Exception e) {
log.error("业务方法执行失败", e);
throw e;
}
log.info("业务方法执行成功");
}
}
5. 性能优化建议
- 使用分页查询避免一次加载大量数据
- 合理使用缓存减少数据库访问
- 批量操作使用saveBatch/updateBatchById
- 复杂查询考虑使用原生SQL
- 大事务拆分为小事务
常见问题
Q: 什么时候选择纯手工开发? A: 当需要精细控制业务逻辑、优化性能、实现复杂查询或集成第三方服务时。
Q: 如何处理多表关联查询? A: 可以使用QueryWrapper的join方法,或在Mapper中编写自定义SQL。
Q: 如何实现动态查询条件? A: 使用QueryWrapper的条件判断,或在XML中使用MyBatis的动态SQL标签。
Q: 事务如何管理? A: 使用@Transactional注解,注意设置rollbackFor = Exception.class。
💡 提示:纯手工开发方式提供最大的灵活性,适合复杂业务场景。合理使用项目提供的工具类和规范,可以在保持灵活性的同时提高开发效率。