springboot通用CURD
最近小朋友开始找工作了, 给他弄了一套通用springboot下的curd, 支持分页
在此记录下;
BaseServiceImpl.java
package data.service.impl;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import common.mybatis.HbblPage;
import data.errors.GlobeError;
import data.model.BaseModel;
import data.service.BaseService;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Transactional;
import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
@Transactional(readOnly = true)
public class BaseServiceImpl<T extends BaseModel> implements BaseService<T> {
private final Class<T> clazzOfT = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
@Autowired
private BaseMapper<T> mapper;
@Autowired
private Snowflake snowflake;
@Override
public T selectOne(Long id) {
return mapper.selectById(id);
}
@Override
@Transactional
public void insert(T model) {
val id = snowflake.nextId();
model.setId(id);
mapper.insert(model);
}
@Override
@Transactional
public void update(Long id, T model) {
model.setId(id);
mapper.updateById(model);
}
@Override
@Transactional
public void delete(Long id) {
try {
val model = clazzOfT.getConstructor().newInstance();
model.setId(id);
model.setDeleted(true);
mapper.updateById(model);
} catch (Exception e) {
log.error(e.getLocalizedMessage(), e);
}
}
@Override
public List<T> selectListByMap(Map<String, Object> map) {
val queryWrapper = mapToQueryWrapper(map, false);
return mapper.selectList(queryWrapper);
}
@Override
public Page<T> selectPageByMap(Map<String, Object> map, Pageable pageable) {
val p = new HbblPage<T>(pageable);
val queryWrapper = mapToQueryWrapper(map, false);
val page = mapper.selectPage(p, queryWrapper);
return page.toPageableResponse();
}
@Override
public Page<T> searchPageByMap(Map<String, Object> map, Pageable pageable) {
val p = new HbblPage<T>(pageable);
val queryWrapper = mapToQueryWrapper(map, true);
val page = mapper.selectPage(p, queryWrapper);
return page.toPageableResponse();
}
@Override
public QueryWrapper<T> mapToQueryWrapper(Map<String, Object> map, boolean or) {
val queryWrapper = new QueryWrapper<T>();
queryWrapper.eq("deleted", false);
Map<String, Object> query = map.entrySet().stream()
.filter(it -> it.getValue() != null
&& !it.getValue().toString().isEmpty()
&& !it.getKey().equals("page")
&& !it.getKey().equals("size")
&& !it.getKey().equals("sort"))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
if (!query.isEmpty()) {
if (or) {
queryWrapper.and(wrapper -> wrappers(wrapper, query, true));
} else {
wrappers(queryWrapper, query, false);
}
}
return queryWrapper;
}
private void wrappers(QueryWrapper<T> queryWrapper, Map<String, Object> map, boolean or) {
map.keySet().forEach(key -> {
// 防止sql注入
if (!key.matches("^(?!_)(?!.*?_$)[a-zA-Z_]+$")) {
// throw GlobeError.SQL_ERROR.buildException();
throw new RuntimeException(GlobeError.SQL_ERROR.getMsg());
}
val property = key.split("_");
if (property.length <= 1) {
queryWrapper.eq(toLowerUnderscore(property[0]), map.get(key));
} else if (map.get(key) == null) {
return;
} else if ("eq".equals(property[1])) {
queryWrapper.eq(toLowerUnderscore(property[0]), map.get(key));
} else if ("li".equals(property[1])) {
queryWrapper.like(toLowerUnderscore(property[0]), "%" + map.get(key) + "%");
} else if ("nl".equals(property[1])) {
queryWrapper.notLike(toLowerUnderscore(property[0]), "%" + map.get(key) + "%");
} else if ("start".equals(property[1])) {
queryWrapper.like(toLowerUnderscore(property[0]), map.get(key) + "%");
} else if ("in".equals(property[1])) {
queryWrapper.in(toLowerUnderscore(property[0]), map.get(key));
} else if ("ni".equals(property[1])) {
queryWrapper.notIn(toLowerUnderscore(property[0]), map.get(key));
} else if ("gt".equals(property[1])) {
queryWrapper.gt(toLowerUnderscore(property[0]), map.get(key));
} else if ("ge".equals(property[1])) {
queryWrapper.ge(toLowerUnderscore(property[0]), map.get(key));
} else if ("lt".equals(property[1])) {
queryWrapper.lt(toLowerUnderscore(property[0]), map.get(key));
} else if ("le".equals(property[1])) {
queryWrapper.le(toLowerUnderscore(property[0]), map.get(key));
} else if ("desc".equals(property[1])) {
queryWrapper.orderByDesc(toLowerUnderscore(property[0]));
} else if ("asc".equals(property[1])) {
queryWrapper.orderByAsc(toLowerUnderscore(property[0]));
} else {
queryWrapper.eq(toLowerUnderscore(property[0]), map.get(key));
}
if (or) {
queryWrapper.or();
}
});
}
private String toLowerUnderscore(String source) {
return StrUtil.toUnderlineCase(source);
}
}
BaseService.java
package data.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import data.model.BaseModel;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import java.util.List;
import java.util.Map;
public interface BaseService<T extends BaseModel> {
T selectOne(Long id);
void insert(T model);
void update(Long id, T model);
void delete(Long id);
List<T> selectListByMap(Map<String, Object> map);
Page<T> selectPageByMap(Map<String, Object> map, Pageable pageable);
/**
* 搜索,现在采用‘或’查询,此方法后续可以接入搜索引擎
*/
Page<T> searchPageByMap(Map<String, Object> map, Pageable pageable);
QueryWrapper<T> mapToQueryWrapper(Map<String, Object> map, boolean or);
}
BaseModel.java
package data.model;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.Getter;
import lombok.Setter;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
@Getter
@Setter
abstract public class BaseModel implements Serializable {
@Serial
private static final long serialVersionUID = -7472715591255438006L;
protected Long id;
@JsonSerialize(using = LocalDateTimeSerializer.class)
protected LocalDateTime createTime;
@JsonSerialize(using = LocalDateTimeSerializer.class)
protected LocalDateTime updateTime;
protected Boolean deleted = false;
}
mapper定义
package data.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hbblkj.pis.data.model.Demo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface DemoMapper extends BaseMapper<Demo> {
}
通用分页 HbblPage.java;
package common.mybatis;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hbblkj.pis.common.spring.PageableRequest;
import com.hbblkj.pis.common.spring.PageableResponse;
import lombok.val;
import org.springframework.data.domain.Pageable;
public class HbblPage<T> extends Page<T> {
public HbblPage() {
}
public HbblPage(Pageable pageable) {
super(pageable.getPageNumber() + 1, pageable.getPageSize());
if (pageable.getSort().isSorted()) {
val orders = pageable.getSort().stream().map((it) -> {
val column = StrUtil.toUnderlineCase(it.getProperty());
val item = new OrderItem();
item.setColumn(column);
item.setAsc(it.isAscending());
return item;
}).toList();
this.setOrders(orders);
}
}
public PageableResponse<T> toPageableResponse() {
PageableRequest pageable = new PageableRequest(Long.valueOf(this.getCurrent() - 1L).intValue(), Long.valueOf(this.getSize()).intValue());
return new PageableResponse<>(this.getRecords(), pageable, this.getTotal());
}
}
公用spring类
package common.spring;
import org.springframework.data.domain.Sort;
import java.lang.reflect.Field;
import java.util.*;
public class FieldsSort extends Sort {
private static final long serialVersionUID = 4240779646538412666L;
private static final FieldOrder DEFAULT_FIX_ORDER = new FieldOrder("_id");
public FieldsSort() {
super(Collections.singletonList(DEFAULT_FIX_ORDER));
setOrders();
}
public FieldsSort(FieldOrder... orders) {
super(Collections.singletonList(DEFAULT_FIX_ORDER));
setOrders(orders);
}
public FieldsSort(List<FieldOrder> orders) {
super(Collections.singletonList(DEFAULT_FIX_ORDER));
setOrders(orders);
}
public FieldsSort(String... properties) {
this(DEFAULT_DIRECTION, properties);
}
public FieldsSort(Direction direction, String... properties) {
this(direction, properties == null ? new ArrayList<>() : Arrays.asList(properties));
}
public FieldsSort(Direction direction, List<String> properties) {
super(Collections.singletonList(DEFAULT_FIX_ORDER));
if (properties == null || properties.isEmpty()) {
setOrders();
return;
}
List<FieldOrder> orders = new ArrayList<>();
for (String property : properties) {
orders.add(new FieldOrder(direction, property));
}
setOrders(orders);
}
@SuppressWarnings("unchecked")
public FieldsSort(Sort sort) {
this();
if (sort == null) return;
try {
Field field = Sort.class.getDeclaredField("orders");
field.setAccessible(true);
List<Order> orders = (List<Order>) field.get(sort);
List<FieldOrder> fieldOrders = new LinkedList<>();
if (orders != null && orders.size() > 0) {
for (Order order : orders) {
if (order != null)
fieldOrders.add(new FieldOrder(order));
}
}
setOrders(fieldOrders);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new IllegalArgumentException(e);
}
}
private void setOrders(List<FieldOrder> orders) {
try {
Field field = Sort.class.getDeclaredField("orders");
field.setAccessible(true);
field.set(this, orders);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new IllegalArgumentException(e);
}
}
private void setOrders(FieldOrder... orders) {
if (orders == null) {
orders = new FieldOrder[0];
}
setOrders(Arrays.asList(orders));
}
public static class FieldOrder extends Order {
private static final long serialVersionUID = -4307899744334924872L;
public FieldOrder() {
this("_id");
}
public FieldOrder(String property) {
super(null, "_id");
setProperties(property);
}
public FieldOrder(Direction direction, String property) {
super(direction, "_id");
setProperties(property);
}
public FieldOrder(Direction direction, String property, NullHandling nullHandlingHint) {
super(direction, "_id", nullHandlingHint);
setProperties(property);
}
public FieldOrder(Order order) {
this();
if (order == null) return;
String[] properties = {"direction", "property", "ignoreCase", "nullHandling"};
try {
for (String property : properties) {
Field field = Order.class.getDeclaredField(property);
field.setAccessible(true);
field.set(this, field.get(order));
}
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new IllegalArgumentException(e);
}
}
private void setProperties(String property) {
Field field;
try {
field = Order.class.getDeclaredField("property");
field.setAccessible(true);
field.set(this, property);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new IllegalArgumentException(e);
}
}
}
}
package common.spring;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import java.io.Serial;
public class PageableRequest extends PageRequest {
@Serial
private static final long serialVersionUID = 6516764632433368424L;
private static final int PAGE_SIZE = 10000;
public static PageableRequest one() {
return new PageableRequest(0, 1);
}
public static PageableRequest max() {
return new PageableRequest(0, PAGE_SIZE - 1);
}
public PageableRequest() {
this(0, 10);
}
public PageableRequest(int page, int size) {
this(page, size < 1 ? 10 : size, new FieldsSort());
}
public PageableRequest(int page, int size, FieldsSort.Direction direction, String... properties) {
this(page, size, new FieldsSort(direction, properties));
}
public PageableRequest(int page, int size, FieldsSort sort) {
super(page, size, sort);
verifyPageSize(size);
}
public PageableRequest(Pageable pageable) {
this(pageable.getPageNumber(), pageable.getPageSize(), new FieldsSort(pageable.getSort()));
}
private void verifyPageSize(int size) {
if (size > PAGE_SIZE) {
throw new IllegalArgumentException("分页参数大小请小于10000");
}
}
}
package common.spring;
import org.springframework.core.MethodParameter;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
public class PageableRequestHandlerMethodArgumentResolver extends PageableHandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return Pageable.class.equals(parameter.getParameterType());
}
@Override
public PageableRequest resolveArgument(
MethodParameter methodParameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {
Pageable pageable = super.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory);
return new PageableRequest(pageable);
}
}
package common.spring;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class PageableResponse<T> extends PageImpl<T> {
private static final long serialVersionUID = -6529648340639543186L;
public PageableResponse() {
super(new ArrayList<>());
}
public PageableResponse(List<T> content, Pageable pageable, long total) {
super(content, pageable, total);
}
public PageableResponse(List<T> content) {
super(content);
}
public static <T> Page<T> empty() {
return empty(Pageable.unpaged());
}
public static <T> Page<T> empty(Pageable pageable) {
return new PageableResponse<>(Collections.emptyList(), pageable, 0L);
}
public static <T> Page<T> fromPage(Page<T> page) {
return new PageableResponse<>(page.getContent(), page.getPageable(), page.getTotalElements());
}
}
错误定义
package data.errors;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public enum GlobeError {
FIND_ERROR("0", "查找失败"),
LOAD_ERROR("0", "获取单个失败"),
SAVE_ERROR("0", "保存失败"),
REMOVE_ERROR("0", "删除失败"),
SQL_ERROR("0", "sql语句错误,可能产生sql注入"),
;
final private String code;
final private String msg;
}

浙公网安备 33010602011771号