yshop-pro init
This commit is contained in:
69
yshop-framework/yshop-spring-boot-starter-mybatis/pom.xml
Normal file
69
yshop-framework/yshop-spring-boot-starter-mybatis/pom.xml
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>co.yixiang.boot</groupId>
|
||||
<artifactId>yshop-framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yshop-spring-boot-starter-mybatis</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>数据库连接池、多数据源、事务、MyBatis 拓展</description>
|
||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>co.yixiang.boot</groupId>
|
||||
<artifactId>yshop-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>co.yixiang.boot</groupId>
|
||||
<artifactId>yshop-spring-boot-starter-web</artifactId>
|
||||
<scope>provided</scope> <!-- 设置为 provided,只有 OncePerRequestFilter 使用到 -->
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.oracle.database.jdbc</groupId>
|
||||
<artifactId>ojdbc8</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 -->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.yulichang</groupId>
|
||||
<artifactId>mybatis-plus-join-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,40 @@
|
||||
package co.yixiang.yshop.framework.datasource.config;
|
||||
|
||||
import co.yixiang.yshop.framework.datasource.core.filter.DruidAdRemoveFilter;
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
/**
|
||||
* 数据库配置类
|
||||
*
|
||||
* @author yshop
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理
|
||||
@EnableConfigurationProperties(DruidStatProperties.class)
|
||||
public class YshopDataSourceAutoConfiguration {
|
||||
|
||||
/**
|
||||
* 创建 DruidAdRemoveFilter 过滤器,过滤 common.js 的广告
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.datasource.druid.web-stat-filter.enabled", havingValue = "true")
|
||||
public FilterRegistrationBean<DruidAdRemoveFilter> druidAdRemoveFilterFilter(DruidStatProperties properties) {
|
||||
// 获取 druid web 监控页面的参数
|
||||
DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
|
||||
// 提取 common.js 的配置路径
|
||||
String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
|
||||
String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
|
||||
// 创建 DruidAdRemoveFilter Bean
|
||||
FilterRegistrationBean<DruidAdRemoveFilter> registrationBean = new FilterRegistrationBean<>();
|
||||
registrationBean.setFilter(new DruidAdRemoveFilter());
|
||||
registrationBean.addUrlPatterns(commonJsPattern);
|
||||
return registrationBean;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package co.yixiang.yshop.framework.datasource.core.enums;
|
||||
|
||||
/**
|
||||
* 对应于多数据源中不同数据源配置
|
||||
*
|
||||
* 通过在方法上,使用 {@link com.baomidou.dynamic.datasource.annotation.DS} 注解,设置使用的数据源。
|
||||
* 注意,默认是 {@link #MASTER} 数据源
|
||||
*
|
||||
* 对应官方文档为 http://dynamic-datasource.com/guide/customize/Annotation.html
|
||||
*/
|
||||
public interface DataSourceEnum {
|
||||
|
||||
/**
|
||||
* 主库,推荐使用 {@link com.baomidou.dynamic.datasource.annotation.Master} 注解
|
||||
*/
|
||||
String MASTER = "master";
|
||||
/**
|
||||
* 从库,推荐使用 {@link com.baomidou.dynamic.datasource.annotation.Slave} 注解
|
||||
*/
|
||||
String SLAVE = "slave";
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package co.yixiang.yshop.framework.datasource.core.filter;
|
||||
|
||||
import com.alibaba.druid.util.Utils;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Druid 底部广告过滤器
|
||||
*
|
||||
* @author yshop
|
||||
*/
|
||||
public class DruidAdRemoveFilter extends OncePerRequestFilter {
|
||||
|
||||
/**
|
||||
* common.js 的路径
|
||||
*/
|
||||
private static final String COMMON_JS_ILE_PATH = "support/http/resources/js/common.js";
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws ServletException, IOException {
|
||||
chain.doFilter(request, response);
|
||||
// 重置缓冲区,响应头不会被重置
|
||||
response.resetBuffer();
|
||||
// 获取 common.js
|
||||
String text = Utils.readFromResource(COMMON_JS_ILE_PATH);
|
||||
// 正则替换 banner, 除去底部的广告信息
|
||||
text = text.replaceAll("<a.*?banner\"></a><br/>", "");
|
||||
text = text.replaceAll("powered.*?shrek.wang</a>", "");
|
||||
response.getWriter().write(text);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
/**
|
||||
* 数据库连接池,采用 Druid
|
||||
* 多数据源,采用爆米花
|
||||
*/
|
||||
package co.yixiang.yshop.framework.datasource;
|
@ -0,0 +1,24 @@
|
||||
package co.yixiang.yshop.framework.expression;
|
||||
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
||||
|
||||
/**
|
||||
* AndExpression 的扩展类(会在原有表达式两端加上括号)
|
||||
*/
|
||||
public class AndExpressionX extends AndExpression {
|
||||
|
||||
public AndExpressionX() {
|
||||
}
|
||||
|
||||
public AndExpressionX(Expression leftExpression, Expression rightExpression) {
|
||||
this.setLeftExpression(leftExpression);
|
||||
this.setRightExpression(rightExpression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + super.toString() + ")";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package co.yixiang.yshop.framework.expression;
|
||||
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
||||
|
||||
/**
|
||||
* OrExpression 的扩展类(会在原有表达式两端加上括号)
|
||||
*/
|
||||
public class OrExpressionX extends OrExpression {
|
||||
|
||||
public OrExpressionX() {
|
||||
}
|
||||
|
||||
public OrExpressionX(Expression leftExpression, Expression rightExpression) {
|
||||
this.setLeftExpression(leftExpression);
|
||||
this.setRightExpression(rightExpression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + super.toString() + ")";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
package co.yixiang.yshop.framework.mybatis.config;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import co.yixiang.yshop.framework.common.util.collection.SetUtils;
|
||||
import co.yixiang.yshop.framework.mybatis.core.enums.SqlConstants;
|
||||
import co.yixiang.yshop.framework.mybatis.core.util.JdbcUtils;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 当 IdType 为 {@link IdType#NONE} 时,根据 PRIMARY 数据源所使用的数据库,自动设置
|
||||
*
|
||||
* @author yshop
|
||||
*/
|
||||
@Slf4j
|
||||
public class IdTypeEnvironmentPostProcessor implements EnvironmentPostProcessor {
|
||||
|
||||
private static final String ID_TYPE_KEY = "mybatis-plus.global-config.db-config.id-type";
|
||||
|
||||
private static final String DATASOURCE_DYNAMIC_KEY = "spring.datasource.dynamic";
|
||||
|
||||
private static final String QUARTZ_JOB_STORE_DRIVER_KEY = "spring.quartz.properties.org.quartz.jobStore.driverDelegateClass";
|
||||
|
||||
private static final Set<DbType> INPUT_ID_TYPES = SetUtils.asSet(DbType.ORACLE, DbType.ORACLE_12C,
|
||||
DbType.POSTGRE_SQL, DbType.KINGBASE_ES, DbType.DB2, DbType.H2);
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
|
||||
// 如果获取不到 DbType,则不进行处理
|
||||
DbType dbType = getDbType(environment);
|
||||
if (dbType == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置 Quartz JobStore 对应的 Driver
|
||||
// TODO yshop:暂时没有找到特别合适的地方,先放在这里
|
||||
setJobStoreDriverIfPresent(environment, dbType);
|
||||
|
||||
// 初始化 SQL 静态变量
|
||||
SqlConstants.init(dbType);
|
||||
|
||||
// 如果非 NONE,则不进行处理
|
||||
IdType idType = getIdType(environment);
|
||||
if (idType != IdType.NONE) {
|
||||
return;
|
||||
}
|
||||
// 情况一,用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库
|
||||
if (INPUT_ID_TYPES.contains(dbType)) {
|
||||
setIdType(environment, IdType.INPUT);
|
||||
return;
|
||||
}
|
||||
// 情况二,自增 ID,适合 MySQL 等直接自增的数据库
|
||||
setIdType(environment, IdType.AUTO);
|
||||
}
|
||||
|
||||
public IdType getIdType(ConfigurableEnvironment environment) {
|
||||
return environment.getProperty(ID_TYPE_KEY, IdType.class);
|
||||
}
|
||||
|
||||
public void setIdType(ConfigurableEnvironment environment, IdType idType) {
|
||||
environment.getSystemProperties().put(ID_TYPE_KEY, idType);
|
||||
log.info("[setIdType][修改 MyBatis Plus 的 idType 为({})]", idType);
|
||||
}
|
||||
|
||||
public void setJobStoreDriverIfPresent(ConfigurableEnvironment environment, DbType dbType) {
|
||||
String driverClass = environment.getProperty(QUARTZ_JOB_STORE_DRIVER_KEY);
|
||||
if (StrUtil.isNotEmpty(driverClass)) {
|
||||
return;
|
||||
}
|
||||
// 根据 dbType 类型,获取对应的 driverClass
|
||||
switch (dbType) {
|
||||
case POSTGRE_SQL:
|
||||
driverClass = "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate";
|
||||
break;
|
||||
case ORACLE:
|
||||
case ORACLE_12C:
|
||||
driverClass = "org.quartz.impl.jdbcjobstore.oracle.OracleDelegate";
|
||||
break;
|
||||
case SQL_SERVER:
|
||||
case SQL_SERVER2005:
|
||||
driverClass = "org.quartz.impl.jdbcjobstore.MSSQLDelegate";
|
||||
break;
|
||||
}
|
||||
// 设置 driverClass 变量
|
||||
if (StrUtil.isNotEmpty(driverClass)) {
|
||||
environment.getSystemProperties().put(QUARTZ_JOB_STORE_DRIVER_KEY, driverClass);
|
||||
}
|
||||
}
|
||||
|
||||
public static DbType getDbType(ConfigurableEnvironment environment) {
|
||||
String primary = environment.getProperty(DATASOURCE_DYNAMIC_KEY + "." + "primary");
|
||||
if (StrUtil.isEmpty(primary)) {
|
||||
return null;
|
||||
}
|
||||
String url = environment.getProperty(DATASOURCE_DYNAMIC_KEY + ".datasource." + primary + ".url");
|
||||
if (StrUtil.isEmpty(url)) {
|
||||
return null;
|
||||
}
|
||||
return JdbcUtils.getDbType(url);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package co.yixiang.yshop.framework.mybatis.config;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import co.yixiang.yshop.framework.mybatis.core.handler.DefaultDBFieldHandler;
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
|
||||
import com.baomidou.mybatisplus.extension.incrementer.H2KeyGenerator;
|
||||
import com.baomidou.mybatisplus.extension.incrementer.KingbaseKeyGenerator;
|
||||
import com.baomidou.mybatisplus.extension.incrementer.OracleKeyGenerator;
|
||||
import com.baomidou.mybatisplus.extension.incrementer.PostgreKeyGenerator;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
/**
|
||||
* MyBaits 配置类
|
||||
*
|
||||
* @author yshop
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@MapperScan(value = "${yshop.info.base-package}", annotationClass = Mapper.class,
|
||||
lazyInitialization = "${mybatis.lazy-initialization:false}") // Mapper 懒加载,目前仅用于单元测试
|
||||
public class YshopMybatisAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
|
||||
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); // 分页插件
|
||||
return mybatisPlusInterceptor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MetaObjectHandler defaultMetaObjectHandler(){
|
||||
return new DefaultDBFieldHandler(); // 自动填充参数类
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "mybatis-plus.global-config.db-config", name = "id-type", havingValue = "INPUT")
|
||||
public IKeyGenerator keyGenerator(ConfigurableEnvironment environment) {
|
||||
DbType dbType = IdTypeEnvironmentPostProcessor.getDbType(environment);
|
||||
if (dbType != null) {
|
||||
switch (dbType) {
|
||||
case POSTGRE_SQL:
|
||||
return new PostgreKeyGenerator();
|
||||
case ORACLE:
|
||||
case ORACLE_12C:
|
||||
return new OracleKeyGenerator();
|
||||
case H2:
|
||||
return new H2KeyGenerator();
|
||||
case KINGBASE_ES:
|
||||
return new KingbaseKeyGenerator();
|
||||
}
|
||||
}
|
||||
// 找不到合适的 IKeyGenerator 实现类
|
||||
throw new IllegalArgumentException(StrUtil.format("DbType{} 找不到合适的 IKeyGenerator 实现类", dbType));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package co.yixiang.yshop.framework.mybatis.core.dataobject;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import lombok.Data;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 基础实体对象
|
||||
*
|
||||
* @author yshop
|
||||
*/
|
||||
@Data
|
||||
public abstract class BaseDO implements Serializable {
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
/**
|
||||
* 最后更新时间
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
/**
|
||||
* 创建者,目前使用 SysUser 的 id 编号
|
||||
*
|
||||
* 使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR)
|
||||
private String creator;
|
||||
/**
|
||||
* 更新者,目前使用 SysUser 的 id 编号
|
||||
*
|
||||
* 使用 String 类型的原因是,未来可能会存在非数值的情况,留好拓展性。
|
||||
*/
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.VARCHAR)
|
||||
private String updater;
|
||||
/**
|
||||
* 是否删除
|
||||
*/
|
||||
@TableLogic
|
||||
private Boolean deleted;
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package co.yixiang.yshop.framework.mybatis.core.enums;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
|
||||
/**
|
||||
* SQL相关常量类
|
||||
*
|
||||
* @author yshop
|
||||
*/
|
||||
public class SqlConstants {
|
||||
|
||||
/**
|
||||
* 数据库的类型
|
||||
*/
|
||||
public static DbType DB_TYPE;
|
||||
|
||||
public static void init(DbType dbType) {
|
||||
DB_TYPE = dbType;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package co.yixiang.yshop.framework.mybatis.core.handler;
|
||||
|
||||
import co.yixiang.yshop.framework.mybatis.core.dataobject.BaseDO;
|
||||
import co.yixiang.yshop.framework.web.core.util.WebFrameworkUtils;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 通用参数填充实现类
|
||||
*
|
||||
* 如果没有显式的对通用参数进行赋值,这里会对通用参数进行填充、赋值
|
||||
*
|
||||
* @author hexiaowu
|
||||
*/
|
||||
public class DefaultDBFieldHandler implements MetaObjectHandler {
|
||||
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) {
|
||||
BaseDO baseDO = (BaseDO) metaObject.getOriginalObject();
|
||||
|
||||
LocalDateTime current = LocalDateTime.now();
|
||||
// 创建时间为空,则以当前时间为插入时间
|
||||
if (Objects.isNull(baseDO.getCreateTime())) {
|
||||
baseDO.setCreateTime(current);
|
||||
}
|
||||
// 更新时间为空,则以当前时间为更新时间
|
||||
if (Objects.isNull(baseDO.getUpdateTime())) {
|
||||
baseDO.setUpdateTime(current);
|
||||
}
|
||||
|
||||
Long userId = WebFrameworkUtils.getLoginUserId();
|
||||
// 当前登录用户不为空,创建人为空,则当前登录用户为创建人
|
||||
if (Objects.nonNull(userId) && Objects.isNull(baseDO.getCreator())) {
|
||||
baseDO.setCreator(userId.toString());
|
||||
}
|
||||
// 当前登录用户不为空,更新人为空,则当前登录用户为更新人
|
||||
if (Objects.nonNull(userId) && Objects.isNull(baseDO.getUpdater())) {
|
||||
baseDO.setUpdater(userId.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
// 更新时间为空,则以当前时间为更新时间
|
||||
Object modifyTime = getFieldValByName("updateTime", metaObject);
|
||||
if (Objects.isNull(modifyTime)) {
|
||||
setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
|
||||
}
|
||||
|
||||
// 当前登录用户不为空,更新人为空,则当前登录用户为更新人
|
||||
Object modifier = getFieldValByName("updater", metaObject);
|
||||
Long userId = WebFrameworkUtils.getLoginUserId();
|
||||
if (Objects.nonNull(userId) && Objects.isNull(modifier)) {
|
||||
setFieldValByName("updater", userId.toString(), metaObject);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
package co.yixiang.yshop.framework.mybatis.core.mapper;
|
||||
|
||||
import co.yixiang.yshop.framework.common.pojo.PageParam;
|
||||
import co.yixiang.yshop.framework.common.pojo.PageResult;
|
||||
import co.yixiang.yshop.framework.mybatis.core.util.MyBatisUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.Db;
|
||||
import com.github.yulichang.base.MPJBaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 在 MyBatis Plus 的 BaseMapper 的基础上拓展,提供更多的能力
|
||||
* <p>
|
||||
* 为什么继承 MPJBaseMapper 接口?支持 MyBatis Plus 多表 Join 的能力。
|
||||
*/
|
||||
public interface BaseMapperX<T> extends MPJBaseMapper<T> {
|
||||
|
||||
default PageResult<T> selectPage(PageParam pageParam, @Param("ew") Wrapper<T> queryWrapper) {
|
||||
// MyBatis Plus 查询
|
||||
IPage<T> mpPage = MyBatisUtils.buildPage(pageParam);
|
||||
selectPage(mpPage, queryWrapper);
|
||||
// 转换返回
|
||||
return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
|
||||
}
|
||||
|
||||
default T selectOne(String field, Object value) {
|
||||
return selectOne(new QueryWrapper<T>().eq(field, value));
|
||||
}
|
||||
|
||||
default T selectOne(SFunction<T, ?> field, Object value) {
|
||||
return selectOne(new LambdaQueryWrapper<T>().eq(field, value));
|
||||
}
|
||||
|
||||
default T selectOne(String field1, Object value1, String field2, Object value2) {
|
||||
return selectOne(new QueryWrapper<T>().eq(field1, value1).eq(field2, value2));
|
||||
}
|
||||
|
||||
default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) {
|
||||
return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
|
||||
}
|
||||
|
||||
default Long selectCount() {
|
||||
return selectCount(new QueryWrapper<T>());
|
||||
}
|
||||
|
||||
default Long selectCount(String field, Object value) {
|
||||
return selectCount(new QueryWrapper<T>().eq(field, value));
|
||||
}
|
||||
|
||||
default Long selectCount(SFunction<T, ?> field, Object value) {
|
||||
return selectCount(new LambdaQueryWrapper<T>().eq(field, value));
|
||||
}
|
||||
|
||||
default List<T> selectList() {
|
||||
return selectList(new QueryWrapper<>());
|
||||
}
|
||||
|
||||
default List<T> selectList(String field, Object value) {
|
||||
return selectList(new QueryWrapper<T>().eq(field, value));
|
||||
}
|
||||
|
||||
default List<T> selectList(SFunction<T, ?> field, Object value) {
|
||||
return selectList(new LambdaQueryWrapper<T>().eq(field, value));
|
||||
}
|
||||
|
||||
default List<T> selectList(String field, Collection<?> values) {
|
||||
return selectList(new QueryWrapper<T>().in(field, values));
|
||||
}
|
||||
|
||||
default List<T> selectList(SFunction<T, ?> field, Collection<?> values) {
|
||||
return selectList(new LambdaQueryWrapper<T>().in(field, values));
|
||||
}
|
||||
|
||||
default List<T> selectList(SFunction<T, ?> leField, SFunction<T, ?> geField, Object value) {
|
||||
return selectList(new LambdaQueryWrapper<T>().le(leField, value).ge(geField, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入,适合大量数据插入
|
||||
*
|
||||
* @param entities 实体们
|
||||
*/
|
||||
default void insertBatch(Collection<T> entities) {
|
||||
Db.saveBatch(entities);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入,适合大量数据插入
|
||||
*
|
||||
* @param entities 实体们
|
||||
* @param size 插入数量 Db.saveBatch 默认为 1000
|
||||
*/
|
||||
default void insertBatch(Collection<T> entities, int size) {
|
||||
Db.saveBatch(entities, size);
|
||||
}
|
||||
|
||||
default void updateBatch(T update) {
|
||||
update(update, new QueryWrapper<>());
|
||||
}
|
||||
|
||||
default void updateBatch(Collection<T> entities, int size) {
|
||||
Db.updateBatchById(entities, size);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
package co.yixiang.yshop.framework.mybatis.core.query;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import co.yixiang.yshop.framework.common.util.collection.ArrayUtils;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 拓展 MyBatis Plus QueryWrapper 类,主要增加如下功能:
|
||||
* <p>
|
||||
* 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。
|
||||
*
|
||||
* @param <T> 数据类型
|
||||
*/
|
||||
public class LambdaQueryWrapperX<T> extends LambdaQueryWrapper<T> {
|
||||
|
||||
public LambdaQueryWrapperX<T> likeIfPresent(SFunction<T, ?> column, String val) {
|
||||
if (StringUtils.hasText(val)) {
|
||||
return (LambdaQueryWrapperX<T>) super.like(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaQueryWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) {
|
||||
if (!CollectionUtils.isEmpty(values)) {
|
||||
return (LambdaQueryWrapperX<T>) super.in(column, values);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaQueryWrapperX<T> inIfPresent(SFunction<T, ?> column, Object... values) {
|
||||
if (!ArrayUtil.isEmpty(values)) {
|
||||
return (LambdaQueryWrapperX<T>) super.in(column, values);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaQueryWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) {
|
||||
if (ObjectUtil.isNotEmpty(val)) {
|
||||
return (LambdaQueryWrapperX<T>) super.eq(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaQueryWrapperX<T> neIfPresent(SFunction<T, ?> column, Object val) {
|
||||
if (ObjectUtil.isNotEmpty(val)) {
|
||||
return (LambdaQueryWrapperX<T>) super.ne(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaQueryWrapperX<T> gtIfPresent(SFunction<T, ?> column, Object val) {
|
||||
if (val != null) {
|
||||
return (LambdaQueryWrapperX<T>) super.gt(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaQueryWrapperX<T> geIfPresent(SFunction<T, ?> column, Object val) {
|
||||
if (val != null) {
|
||||
return (LambdaQueryWrapperX<T>) super.ge(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaQueryWrapperX<T> ltIfPresent(SFunction<T, ?> column, Object val) {
|
||||
if (val != null) {
|
||||
return (LambdaQueryWrapperX<T>) super.lt(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaQueryWrapperX<T> leIfPresent(SFunction<T, ?> column, Object val) {
|
||||
if (val != null) {
|
||||
return (LambdaQueryWrapperX<T>) super.le(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaQueryWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object val1, Object val2) {
|
||||
if (val1 != null && val2 != null) {
|
||||
return (LambdaQueryWrapperX<T>) super.between(column, val1, val2);
|
||||
}
|
||||
if (val1 != null) {
|
||||
return (LambdaQueryWrapperX<T>) ge(column, val1);
|
||||
}
|
||||
if (val2 != null) {
|
||||
return (LambdaQueryWrapperX<T>) le(column, val2);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaQueryWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object[] values) {
|
||||
Object val1 = ArrayUtils.get(values, 0);
|
||||
Object val2 = ArrayUtils.get(values, 1);
|
||||
return betweenIfPresent(column, val1, val2);
|
||||
}
|
||||
|
||||
// ========== 重写父类方法,方便链式调用 ==========
|
||||
|
||||
@Override
|
||||
public LambdaQueryWrapperX<T> eq(boolean condition, SFunction<T, ?> column, Object val) {
|
||||
super.eq(condition, column, val);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LambdaQueryWrapperX<T> eq(SFunction<T, ?> column, Object val) {
|
||||
super.eq(column, val);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LambdaQueryWrapperX<T> orderByDesc(SFunction<T, ?> column) {
|
||||
super.orderByDesc(true, column);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LambdaQueryWrapperX<T> last(String lastSql) {
|
||||
super.last(lastSql);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LambdaQueryWrapperX<T> in(SFunction<T, ?> column, Collection<?> coll) {
|
||||
super.in(column, coll);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
package co.yixiang.yshop.framework.mybatis.core.query;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import co.yixiang.yshop.framework.mybatis.core.enums.SqlConstants;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 拓展 MyBatis Plus QueryWrapper 类,主要增加如下功能:
|
||||
*
|
||||
* 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。
|
||||
*
|
||||
* @param <T> 数据类型
|
||||
*/
|
||||
public class QueryWrapperX<T> extends QueryWrapper<T> {
|
||||
|
||||
public QueryWrapperX<T> likeIfPresent(String column, String val) {
|
||||
if (StringUtils.hasText(val)) {
|
||||
return (QueryWrapperX<T>) super.like(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperX<T> inIfPresent(String column, Collection<?> values) {
|
||||
if (!CollectionUtils.isEmpty(values)) {
|
||||
return (QueryWrapperX<T>) super.in(column, values);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperX<T> inIfPresent(String column, Object... values) {
|
||||
if (!ArrayUtils.isEmpty(values)) {
|
||||
return (QueryWrapperX<T>) super.in(column, values);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperX<T> eqIfPresent(String column, Object val) {
|
||||
if (val != null) {
|
||||
return (QueryWrapperX<T>) super.eq(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperX<T> neIfPresent(String column, Object val) {
|
||||
if (val != null) {
|
||||
return (QueryWrapperX<T>) super.ne(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperX<T> gtIfPresent(String column, Object val) {
|
||||
if (val != null) {
|
||||
return (QueryWrapperX<T>) super.gt(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperX<T> geIfPresent(String column, Object val) {
|
||||
if (val != null) {
|
||||
return (QueryWrapperX<T>) super.ge(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperX<T> ltIfPresent(String column, Object val) {
|
||||
if (val != null) {
|
||||
return (QueryWrapperX<T>) super.lt(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperX<T> leIfPresent(String column, Object val) {
|
||||
if (val != null) {
|
||||
return (QueryWrapperX<T>) super.le(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperX<T> betweenIfPresent(String column, Object val1, Object val2) {
|
||||
if (val1 != null && val2 != null) {
|
||||
return (QueryWrapperX<T>) super.between(column, val1, val2);
|
||||
}
|
||||
if (val1 != null) {
|
||||
return (QueryWrapperX<T>) ge(column, val1);
|
||||
}
|
||||
if (val2 != null) {
|
||||
return (QueryWrapperX<T>) le(column, val2);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperX<T> betweenIfPresent(String column, Object[] values) {
|
||||
if (values!= null && values.length != 0 && values[0] != null && values[1] != null) {
|
||||
return (QueryWrapperX<T>) super.between(column, values[0], values[1]);
|
||||
}
|
||||
if (values!= null && values.length != 0 && values[0] != null) {
|
||||
return (QueryWrapperX<T>) ge(column, values[0]);
|
||||
}
|
||||
if (values!= null && values.length != 0 && values[1] != null) {
|
||||
return (QueryWrapperX<T>) le(column, values[1]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// ========== 重写父类方法,方便链式调用 ==========
|
||||
|
||||
@Override
|
||||
public QueryWrapperX<T> eq(boolean condition, String column, Object val) {
|
||||
super.eq(condition, column, val);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryWrapperX<T> eq(String column, Object val) {
|
||||
super.eq(column, val);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryWrapperX<T> orderByDesc(String column) {
|
||||
super.orderByDesc(true, column);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryWrapperX<T> last(String lastSql) {
|
||||
super.last(lastSql);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryWrapperX<T> in(String column, Collection<?> coll) {
|
||||
super.in(column, coll);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置只返回最后一条
|
||||
*
|
||||
* TODO yshop:不是完美解,需要在思考下。如果使用多数据源,并且数据源是多种类型时,可能会存在问题:实现之返回一条的语法不同
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public QueryWrapperX<T> limitN(int n) {
|
||||
Assert.notNull(SqlConstants.DB_TYPE, "获取不到数据库的类型");
|
||||
switch (SqlConstants.DB_TYPE) {
|
||||
case ORACLE:
|
||||
case ORACLE_12C:
|
||||
super.eq("ROWNUM", n);
|
||||
break;
|
||||
case SQL_SERVER:
|
||||
case SQL_SERVER2005:
|
||||
super.select("TOP " + n + " *"); // 由于 SQL Server 是通过 SELECT TOP 1 实现限制一条,所以只好使用 * 查询剩余字段
|
||||
break;
|
||||
default:
|
||||
super.last("LIMIT " + n);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package co.yixiang.yshop.framework.mybatis.core.type;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.symmetric.AES;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import org.apache.ibatis.type.BaseTypeHandler;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* 字段字段的 TypeHandler 实现类,基于 {@link cn.hutool.crypto.symmetric.AES} 实现
|
||||
* 可通过 jasypt.encryptor.password 配置项,设置密钥
|
||||
*
|
||||
* @author yshop
|
||||
*/
|
||||
public class EncryptTypeHandler extends BaseTypeHandler<String> {
|
||||
|
||||
private static final String ENCRYPTOR_PROPERTY_NAME = "mybatis-plus.encryptor.password";
|
||||
|
||||
private static AES aes;
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
|
||||
ps.setString(i, encrypt(parameter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
String value = rs.getString(columnName);
|
||||
return decrypt(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
String value = rs.getString(columnIndex);
|
||||
return decrypt(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
String value = cs.getString(columnIndex);
|
||||
return decrypt(value);
|
||||
}
|
||||
|
||||
private static String decrypt(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return getEncryptor().decryptStr(value);
|
||||
}
|
||||
|
||||
public static String encrypt(String rawValue) {
|
||||
if (rawValue == null) {
|
||||
return null;
|
||||
}
|
||||
return getEncryptor().encryptBase64(rawValue);
|
||||
}
|
||||
|
||||
private static AES getEncryptor() {
|
||||
if (aes != null) {
|
||||
return aes;
|
||||
}
|
||||
// 构建 AES
|
||||
String password = SpringUtil.getProperty(ENCRYPTOR_PROPERTY_NAME);
|
||||
Assert.notEmpty(password, "配置项({}) 不能为空", ENCRYPTOR_PROPERTY_NAME);
|
||||
aes = SecureUtil.aes(password.getBytes());
|
||||
return aes;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package co.yixiang.yshop.framework.mybatis.core.type;
|
||||
|
||||
import co.yixiang.yshop.framework.common.util.json.JsonUtils;
|
||||
import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 参考 {@link com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler} 实现
|
||||
* 在我们将字符串反序列化为 Set 并且泛型为 Long 时,如果每个元素的数值太小,会被处理成 Integer 类型,导致可能存在隐性的 BUG。
|
||||
*
|
||||
* 例如说哦,SysUserDO 的 postIds 属性
|
||||
*
|
||||
* @author yshop
|
||||
*/
|
||||
public class JsonLongSetTypeHandler extends AbstractJsonTypeHandler<Object> {
|
||||
|
||||
private static final TypeReference<Set<Long>> TYPE_REFERENCE = new TypeReference<Set<Long>>(){};
|
||||
|
||||
@Override
|
||||
protected Object parse(String json) {
|
||||
return JsonUtils.parseObject(json, TYPE_REFERENCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toJson(Object obj) {
|
||||
return JsonUtils.toJsonString(obj);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package co.yixiang.yshop.framework.mybatis.core.type;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import co.yixiang.yshop.framework.common.util.string.StrUtils;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||
import org.apache.ibatis.type.MappedTypes;
|
||||
import org.apache.ibatis.type.TypeHandler;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* List<Long> 的类型转换器实现类,对应数据库的 varchar 类型
|
||||
*
|
||||
* @author yshop
|
||||
*/
|
||||
@MappedJdbcTypes(JdbcType.VARCHAR)
|
||||
@MappedTypes(List.class)
|
||||
public class LongListTypeHandler implements TypeHandler<List<Long>> {
|
||||
|
||||
private static final String COMMA = ",";
|
||||
|
||||
@Override
|
||||
public void setParameter(PreparedStatement ps, int i, List<Long> strings, JdbcType jdbcType) throws SQLException {
|
||||
// 设置占位符
|
||||
ps.setString(i, CollUtil.join(strings, COMMA));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getResult(ResultSet rs, String columnName) throws SQLException {
|
||||
String value = rs.getString(columnName);
|
||||
return getResult(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
String value = rs.getString(columnIndex);
|
||||
return getResult(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
String value = cs.getString(columnIndex);
|
||||
return getResult(value);
|
||||
}
|
||||
|
||||
private List<Long> getResult(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return StrUtils.splitToLong(value, COMMA);
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package co.yixiang.yshop.framework.mybatis.core.type;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.MappedJdbcTypes;
|
||||
import org.apache.ibatis.type.MappedTypes;
|
||||
import org.apache.ibatis.type.TypeHandler;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* List<String> 的类型转换器实现类,对应数据库的 varchar 类型
|
||||
*
|
||||
* @author 永不言败
|
||||
* @since 2022 3/23 12:50:15
|
||||
*/
|
||||
@MappedJdbcTypes(JdbcType.VARCHAR)
|
||||
@MappedTypes(List.class)
|
||||
public class StringListTypeHandler implements TypeHandler<List<String>> {
|
||||
|
||||
private static final String COMMA = ",";
|
||||
|
||||
@Override
|
||||
public void setParameter(PreparedStatement ps, int i, List<String> strings, JdbcType jdbcType) throws SQLException {
|
||||
// 设置占位符
|
||||
ps.setString(i, CollUtil.join(strings, COMMA));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getResult(ResultSet rs, String columnName) throws SQLException {
|
||||
String value = rs.getString(columnName);
|
||||
return getResult(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
String value = rs.getString(columnIndex);
|
||||
return getResult(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
String value = cs.getString(columnIndex);
|
||||
return getResult(value);
|
||||
}
|
||||
|
||||
private List<String> getResult(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return StrUtil.splitTrim(value, COMMA);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package co.yixiang.yshop.framework.mybatis.core.util;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
|
||||
/**
|
||||
* JDBC 工具类
|
||||
*
|
||||
* @author yshop
|
||||
*/
|
||||
public class JdbcUtils {
|
||||
|
||||
/**
|
||||
* 判断连接是否正确
|
||||
*
|
||||
* @param url 数据源连接
|
||||
* @param username 账号
|
||||
* @param password 密码
|
||||
* @return 是否正确
|
||||
*/
|
||||
public static boolean isConnectionOK(String url, String username, String password) {
|
||||
try (Connection ignored = DriverManager.getConnection(url, username, password)) {
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 URL 对应的 DB 类型
|
||||
*
|
||||
* @param url URL
|
||||
* @return DB 类型
|
||||
*/
|
||||
public static DbType getDbType(String url) {
|
||||
String name = com.alibaba.druid.util.JdbcUtils.getDbType(url, null);
|
||||
return DbType.getDbType(name);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package co.yixiang.yshop.framework.mybatis.core.util;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import co.yixiang.yshop.framework.common.pojo.PageParam;
|
||||
import co.yixiang.yshop.framework.common.pojo.SortingField;
|
||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import net.sf.jsqlparser.expression.Alias;
|
||||
import net.sf.jsqlparser.schema.Column;
|
||||
import net.sf.jsqlparser.schema.Table;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* MyBatis 工具类
|
||||
*/
|
||||
public class MyBatisUtils {
|
||||
|
||||
private static final String MYSQL_ESCAPE_CHARACTER = "`";
|
||||
|
||||
public static <T> Page<T> buildPage(PageParam pageParam) {
|
||||
return buildPage(pageParam, null);
|
||||
}
|
||||
|
||||
public static <T> Page<T> buildPage(PageParam pageParam, Collection<SortingField> sortingFields) {
|
||||
// 页码 + 数量
|
||||
Page<T> page = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
|
||||
// 排序字段
|
||||
if (!CollectionUtil.isEmpty(sortingFields)) {
|
||||
page.addOrder(sortingFields.stream().map(sortingField -> SortingField.ORDER_ASC.equals(sortingField.getOrder()) ?
|
||||
OrderItem.asc(sortingField.getField()) : OrderItem.desc(sortingField.getField()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将拦截器添加到链中
|
||||
* 由于 MybatisPlusInterceptor 不支持添加拦截器,所以只能全量设置
|
||||
*
|
||||
* @param interceptor 链
|
||||
* @param inner 拦截器
|
||||
* @param index 位置
|
||||
*/
|
||||
public static void addInterceptor(MybatisPlusInterceptor interceptor, InnerInterceptor inner, int index) {
|
||||
List<InnerInterceptor> inners = new ArrayList<>(interceptor.getInterceptors());
|
||||
inners.add(index, inner);
|
||||
interceptor.setInterceptors(inners);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得 Table 对应的表名
|
||||
*
|
||||
* 兼容 MySQL 转义表名 `t_xxx`
|
||||
*
|
||||
* @param table 表
|
||||
* @return 去除转移字符后的表名
|
||||
*/
|
||||
public static String getTableName(Table table) {
|
||||
String tableName = table.getName();
|
||||
if (tableName.startsWith(MYSQL_ESCAPE_CHARACTER) && tableName.endsWith(MYSQL_ESCAPE_CHARACTER)) {
|
||||
tableName = tableName.substring(1, tableName.length() - 1);
|
||||
}
|
||||
return tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 Column 对象
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @param tableAlias 别名
|
||||
* @param column 字段名
|
||||
* @return Column 对象
|
||||
*/
|
||||
public static Column buildColumn(String tableName, Alias tableAlias, String column) {
|
||||
if (tableAlias != null) {
|
||||
tableName = tableAlias.getName();
|
||||
}
|
||||
return new Column(tableName + StringPool.DOT + column);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 使用 MyBatis Plus 提升使用 MyBatis 的开发效率
|
||||
*/
|
||||
package co.yixiang.yshop.framework.mybatis;
|
@ -0,0 +1 @@
|
||||
package co.yixiang.yshop.framework;
|
@ -0,0 +1,2 @@
|
||||
org.springframework.boot.env.EnvironmentPostProcessor=\
|
||||
co.yixiang.yshop.framework.mybatis.config.IdTypeEnvironmentPostProcessor
|
@ -0,0 +1,2 @@
|
||||
co.yixiang.yshop.framework.datasource.config.YshopDataSourceAutoConfiguration
|
||||
co.yixiang.yshop.framework.mybatis.config.YshopMybatisAutoConfiguration
|
@ -0,0 +1 @@
|
||||
<https://www.yixiang.co/Spring-Boot/MyBatis/?yshop>
|
@ -0,0 +1 @@
|
||||
<https://www.yixiang.co/Spring-Boot/dynamic-datasource/?yshop>
|
@ -0,0 +1 @@
|
||||
<https://www.yixiang.co/Spring-Boot/datasource-pool/?yshop>
|
Reference in New Issue
Block a user