首页
归档
留言
广告合作
友链
美女主播
Search
1
博瑞GE车机升级/降级
5,146 阅读
2
Mac打印机设置黑白打印
4,517 阅读
3
修改elementUI中el-table树形结构图标
4,516 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,351 阅读
5
intelliJ Idea 2022.2.X破解
4,060 阅读
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Spring Cloud
Mac
mybatis
WordPress
Nacos
Spring Cloud Alibaba
Mybatis-Plus
jQuery
Java Script
asp.net
微信小程序
Sentinel
UniApp
MySQL
asp.net core
IntelliJ IDEA
Jpa
树莓派
Laughing
累计撰写
570
篇文章
累计收到
1,424
条评论
首页
栏目
Java
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
广告合作
友链
美女主播
搜索到
15
篇与
的结果
2022-02-06
MyBatis在xml文件中使用中文字符
大多数情况下,我们在xml中使用if标签的时候,都是判断是否为空或null,像下面这样<if test="carId != null and carId != ''"> and condition </if>但是有时候,我们比较的值可能是字符甚至汉字,其实字符也还好说,正常使用即可,像下面这样<if test="carId != null and carId != 'A'"> and condition </if>但是如果你使用的是汉字,那么事情就没那么简单了。对于汉字的比较,我们可以像下面这样处理<if test="oilType != null and '电'.toString() neq oilType"> and oil_type <![CDATA[<>]]> #{oilType} </if>注意看上面,在汉字后面要加上.toString(),不然会报错。
2022年02月06日
1,858 阅读
0 评论
1 点赞
2022-02-02
MyBatis-Plus更新部分字段
使用MyBatis-Plus时,有时候我们在更新实体时,可能只想更新部分字段。下面介绍两种更新部分字段的方法。一、先查询后更新的方式这种方式不是很好,说白了就是先执行一遍查询,查询到实体后,设置修改的属性,再次调用update方法更新,这样MyBatis只会更新修改的字段。二、通过UpdateWrapper更新 LambdaUpdateWrapper<OilUser> userUpdateWrapper = new LambdaUpdateWrapper<>(); userUpdateWrapper.set(OilUser::getUsername, user.getUsername()); userUpdateWrapper.set(OilUser::getNickName, user.getNickName()); userUpdateWrapper.set(OilUser::getTel, user.getTel()); userUpdateWrapper.set(OilUser::getEmail, user.getEmail()); if (!StringUtils.isEmpty(user.getPassword())) { userUpdateWrapper.set(OilUser::getPassword, passwordEncoder.encode(user.getPassword())); } userUpdateWrapper.eq(OilUser::getId, user.getId()); oilUserMapper.update(null, userUpdateWrapper);注意oilUserMapper.update(null, userUpdateWrapper);,第一个参数一定要设置null,这样就只会更新你set的字段。
2022年02月02日
2,263 阅读
0 评论
3 点赞
2021-06-11
Spring Boot 使用mybatis-plus逻辑删除选装件
试想一下我们的系统,我们所有的表都有五个字段,分别是创建者(create_by)、创建时间(create_time)、修改者(update_by)、修改时间(update_time)、删除标志(del_flag), 同时通过mybatis拦截器,自动注入创建人、创建时间、修改人、修改时间。目前在数据库新增,能自动注入创建人、创建时间,修改时,能自动注入修改人、 修改时间。如果我们调用mybatis-plus提供的删除方法(deleteById),既然是逻辑删除,我们设想的肯定是此时能够自动注入修改人、修改时间,但是,实际测试你会发现,此时sql并没有注入修改人、修改时间。为了解决这个问题,mybatis-plus提供了一个LogicDeleteByIdWithFill的逻辑删除组装件。通过此方法,我们在进行逻辑删除时,便能自动注入修改人、修改时间。继承DefaultSqlInjector/** * Description:官方逻辑删除选装件 * * @author : laughing * DateTime: 2021-06-11 15:21 */ @Component public class MySqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList(Class<?> mapperClass) { List<AbstractMethod> methodList = super.getMethodList(mapperClass); //2.官方选装件,逻辑删除时,自动填充其他字段(例如逻辑删除时,自动填充删除人是谁,什么时候删除的) methodList.add(new LogicDeleteByIdWithFill()); return methodList; } }配置逻辑删除插件/** * Mybatis-Plus配置 * * @author laughing */ @Configuration @EnableTransactionManagement public class MyBatisPlusConfig { @javax.annotation.Resource private Environment env; static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; private final Logger logger = LoggerFactory.getLogger(MyBatisPlusConfig.class); @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor // 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false // interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); interceptor.addInnerInterceptor(new TenantLineInnerInterceptor( new TenantLineHandler() { // manager_id = 1088248166370832385 // 获取租户 ID 值表达式,只支持单个 ID 值 @Override public Expression getTenantId() { return new StringValue(SecurityUtils.getOrgCode()); } // 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件, // 这里设置 role表不需要该条件 @Override public boolean ignoreTable(String tableName) { logger.info("orgCode=======" + SecurityUtils.getOrgCode()); return StringUtils.isEmpty(SecurityUtils.getOrgCode()) || WisdomAgricultureConfig.getFilterTableList().stream().anyMatch(e -> e.equalsIgnoreCase(tableName)); } @Override public String getTenantIdColumn() { return "org_code"; } })); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> configuration.setUseDeprecatedExecutor(false); } public static String setTypeAliasesPackage(String typeAliasesPackage) { ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver(); MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); List<String> allResult = new ArrayList<String>(); try { for (String aliasesPackage : typeAliasesPackage.split(",")) { List<String> result = new ArrayList<String>(); aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN; Resource[] resources = resolver.getResources(aliasesPackage); if (resources != null && resources.length > 0) { MetadataReader metadataReader = null; for (Resource resource : resources) { if (resource.isReadable()) { metadataReader = metadataReaderFactory.getMetadataReader(resource); try { result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } if (result.size() > 0) { HashSet<String> hashResult = new HashSet<String>(result); allResult.addAll(hashResult); } } if (allResult.size() > 0) { typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0])); } else { throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包"); } } catch (IOException e) { e.printStackTrace(); } return typeAliasesPackage; } public Resource[] resolveMapperLocations(String[] mapperLocations) { ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); List<Resource> resources = new ArrayList<Resource>(); if (mapperLocations != null) { for (String mapperLocation : mapperLocations) { try { Resource[] mappers = resourceResolver.getResources(mapperLocation); resources.addAll(Arrays.asList(mappers)); } catch (IOException e) { // ignore } } } return resources.toArray(new Resource[resources.size()]); } @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage"); String mapperLocations = env.getProperty("mybatis-plus.mapperLocations"); String configLocation = env.getProperty("mybatis-plus.configLocation"); typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); VFS.addImplClass(SpringBootVFS.class); final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setTypeAliasesPackage(typeAliasesPackage); sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); sessionFactory.setPlugins(mybatisSqlInterceptor(), mybatisPlusInterceptor()); sessionFactory.setGlobalConfig(globalConfig()); return sessionFactory.getObject(); } @Bean public MybatisSqlInterceptor mybatisSqlInterceptor() { MybatisSqlInterceptor mybatisSqlInterceptor = new MybatisSqlInterceptor(); Properties properties = new Properties(); mybatisSqlInterceptor.setProperties(properties); return mybatisSqlInterceptor; } /** * 逻辑删除插件 */ @Bean public GlobalConfig globalConfig() { GlobalConfig globalConfig = new GlobalConfig(); GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig(); dbConfig.setLogicDeleteValue("Y"); dbConfig.setLogicNotDeleteValue("N"); globalConfig.setDbConfig(dbConfig); globalConfig.setSqlInjector(sqlInjector()); return globalConfig; } /** * 自定义Sql注入器 */ @Bean public MySqlInjector sqlInjector() { return new MySqlInjector(); } }扩展BaseMapper/** * Description: * * @author : laughing * DateTime: 2021-06-11 15:23 */ public interface MyBaseMapper<T> extends BaseMapper<T> { /** * 逻辑删除,并且带自动填充 */ int deleteByIdWithFill(T entity); }修改Mapper接口修改Mapper接口,将原来继承BaseMapper改成我们自定义的MyBaseMapper/** * Description:红外测温 * * @author : laughing * DateTime: 2021-05-18 10:28 */ public interface CulturePigTemperatureMapper extends MyBaseMapper<CulturePigTemperature> { /** * 红外测温列表查询 * @param pig 查询条件 * @return 结果 */ List<CulturePigTemperature> selectCulturePigTemperatureList(CulturePig pig); }修改删除方法将原来的删除方法deleteById改成deleteByIdWithFill再次测试,可以看到修改人、修改时间字段能够在逻辑删除方法时自动注入。
2021年06月11日
1,688 阅读
0 评论
0 点赞
2021-05-28
spring boot mybatis配置主从数据库(多数据源)
MyBatis配置多数据源基本步骤:预制两个测试的数据库master和cluster添加mybatis及druid依赖配置文件配置两个数据源(配置数据源时,必须要有一个主数据源)测试代码预制数据库表预制两个数据库,主库为master,并创建表test,从库为cluster,并创建表user。主库从库创建表语句:CREATE TABLE `test` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4预制数据INSERT INTO cluster.user (id, username) VALUES (1, '李四');从库从库创建表语句CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4预制语句INSERT INTO cluster.user (id, username) VALUES (1, '李四');添加项目依赖创建spring boot项目,并添加如下依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.4</version> </dependency>添加配置数据库连接在application.properties中添加两个数据库的配置文件#主库配置 master.datasource.url=jdbc:mysql://IP:3306/master?useSSL=false&characterEncoding=utf8 master.datasource.username=master master.datasource.password=master master.datasource.driver=com.mysql.jdbc.Driver #从库配置 cluster.datasource.url=jdbc:mysql://IP:3306/cluster?useSSL=false&characterEncoding=utf8 cluster.datasource.username=cluster cluster.datasource.password=cluster cluster.datasource.driver=com.mysql.jdbc.Driver添加数据源主库添加主库数据源MasterDataSourceConfig.javapackage net.xiangcaowuyu.mybatsmultidatasource.config.ds; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; /** * 主库配置 * * @author laughing @2021.5.27 */ @Configuration @MapperScan(basePackages = MasterDataSourceConfig.PACKAGE,sqlSessionFactoryRef = "masterSqlSessionFactory") public class MasterDataSourceConfig { final static String PACKAGE = "net.xiangcaowuyu.mybatsmultidatasource.dao.master"; private final static String mapperLocation = "classpath*:mapper/**/*.xml"; @Value("${master.datasource.url}") private String url; @Value("${master.datasource.username}") private String username; @Value("${master.datasource.password}") private String password; @Value("${master.datasource.driver}") private String driver; @Bean(name = "masterDataSource") @Primary public DataSource masterDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setDriverClassName(driver); return dataSource; } @Bean(name = "masterDataSourceTransactionManager") @Primary public DataSourceTransactionManager masterDataSourceTransactionManager() { return new DataSourceTransactionManager(masterDataSource()); } @Bean(name = "masterSqlSessionFactory") @Primary public SqlSessionFactory masterSqlSessionFactory(@Qualifier(value = "masterDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MasterDataSourceConfig.mapperLocation)); return sqlSessionFactoryBean.getObject(); } } 从库添加从库数据源ClusterDataSourceConfig.javapackage net.xiangcaowuyu.mybatsmultidatasource.config.ds; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; /** * 从库配置 * * @author laughing @2021.5.27 */ @Configuration @MapperScan(basePackages = ClusterDataSourceConfig.PACKAGE,sqlSessionFactoryRef = "clusterSqlSessionFactory") public class ClusterDataSourceConfig { final static String PACKAGE = "net.xiangcaowuyu.mybatsmultidatasource.dao.cluster"; private final static String mapperLocation = "classpath*:mapper/**/*.xml"; @Value("${cluster.datasource.url}") private String url; @Value("${cluster.datasource.username}") private String username; @Value("${cluster.datasource.password}") private String password; @Value("${cluster.datasource.driver}") private String driver; @Bean(name = "clusterDataSource") @Primary public DataSource clusterDataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setDriverClassName(driver); return dataSource; } @Bean(name = "clusterDataSourceTransactionManager") @Primary public DataSourceTransactionManager clusterDataSourceTransactionManager() { return new DataSourceTransactionManager(clusterDataSource()); } @Bean(name = "clusterSqlSessionFactory") @Primary public SqlSessionFactory masterSqlSessionFactory(@Qualifier(value = "clusterDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(ClusterDataSourceConfig.mapperLocation)); return sqlSessionFactoryBean.getObject(); } } 主库、从库的数据源通过@MapperScan注解注入不同数据库mapper所在的包。创建DAO主库主库的包位于net.xiangcaowuyu.mybatsmultidatasource.dao.master与@MapperScan配置的位置要保持一致。主库的包里面创建一个selectAll()方法,用于查询所有数据,及查询主库Test表的所有数据。从库从库的包位于net.xiangcaowuyu.mybatsmultidatasource.dao.cluster与@MapperScan配置的位置要保持一致。从库的包里面创建一个selectAll()方法,用于查询所有数据,及查询主库User表的所有数据。创建Service代码就不罗列了,也是有一个selectAll()方法。测试创建controller分别查询主库及从库的数据,如下@RestController public class UserController { @Resource private ITestService testService; @Resource private IUserService userService; @GetMapping("/master") public List<Test> getMasterAll(){ return testService.selectAll(); } @GetMapping("/cluster") public List<User> getClusterAll(){ return userService.selectAll(); } }
2021年05月28日
1,560 阅读
0 评论
0 点赞
2021-05-11
mybatis-plus逻辑删除不生效的解决办法
我们在使用mybatis-plus时,一般设备逻辑删除是非常简单的,基本上在yaml等配置文件中做一下配置。然后在字段上注解@TableLogic就可以了。有不清楚的,可以参考https://www.xiangcaowuyu.net/java/mybatis-plus-logical-deletion.html但是今天在项目中,发现一个问题,就是明明也正确的进行了配置,但是在进行数据库操作时,发现逻辑删除并没有生效。问题描述先说一下问题先想,数据库指定的字段可以使用,但是指定是否逻辑删除的值时还是mybatis-plus默认的0和1,并不是我指定的N和Y。配置文件先来看下我的配置文件。mybatisPlus: # 搜索指定包别名 typeAliasesPackage: net.xiangcaowuyu.**.domain # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml global-config: db-config: # 配置逻辑删除 logic-delete-field: del_flag logic-not-delete-value: N logic-delete-value: Y通过配置文件,我指定数据库标记逻辑删除的字段为del_flag,如果已经删除,标记为Y,如果没有删除(默认值)就是N。实体通过提取的公共实体,标记逻辑删除字段,如下@Data public class BaseEntity implements Serializable { private static final long serialVersionUID = 1L; /** * 搜索值 */ private String searchValue; /** * 创建者 */ @TableField(fill = FieldFill.INSERT) private Long createBy; /** * 创建时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @TableField(fill = FieldFill.INSERT) private Date createTime; /** * 更新者 */ @TableField(fill = FieldFill.UPDATE) private Long updateBy; /** * 更新时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @TableField(fill = FieldFill.UPDATE) private Date updateTime; /** * 删除标志(Y-已删除,N-未删除) */ @TableLogic private String delFlag; /** * 开始时间 */ @JsonIgnore @TableField(exist = false) private String beginTime; /** * 结束时间 */ @JsonIgnore @TableField(exist = false) private String endTime; }使用调用了一个update方法postMapper.updatePost(post);在进行更新操作时,mybatis-plus会追加where条件防止更新到已删除数据,且使用wrapper.entity生成的where条件会忽略该字段。也就是说,我本来的方法对应的sql可能是update xx set xx where xx=xx如果我配置的逻辑删除没有问题的话,mybatis-plus生成的sql应该是update xx set xx where xx=xx and del_flag = 'N'但是实际我测试发现,生成的sql却是update xx set xx where xx=xx and del_flag = '0'可以看到,虽然逻辑删除的字段是对的,但是实际上,对应字段是否删除的值还是mybatis-plus默认的,并不是我们设置的。问题分析其实这个问题之前还是好的,让我想到应该是最近配置的SqlSessionFactory的问题。 @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage"); String mapperLocations = env.getProperty("mybatis-plus.mapperLocations"); String configLocation = env.getProperty("mybatis-plus.configLocation"); typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); VFS.addImplClass(SpringBootVFS.class); final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean (); sessionFactory.setDataSource(dataSource); sessionFactory.setTypeAliasesPackage(typeAliasesPackage); sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); sessionFactory.setPlugins(mybatisSqlInterceptor(),mybatisPlusInterceptor()); return sessionFactory.getObject(); }我这里重新注入了MybatisSqlSessionFactoryBean,但是并没有对它的配置进行修改,这就导致了我配置文件里的东西并没有加载。解决解决办法也很简单,两种方式我们分别说下。方式一方式一是在我们实体逻辑删除的注解上加上删除和未删除对应的值。 /** * 删除标志(Y-已删除,N-未删除) */ @TableLogic(value = "N",delval = "Y") private String delFlag;方式二方式二就是,我们在MybatisSqlSessionFactoryBean的bean里,把我们配置文件里的配置加上。对SqlSessionFactory改造如下 @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage"); String mapperLocations = env.getProperty("mybatis-plus.mapperLocations"); String configLocation = env.getProperty("mybatis-plus.configLocation"); typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); VFS.addImplClass(SpringBootVFS.class); final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean (); sessionFactory.setDataSource(dataSource); sessionFactory.setTypeAliasesPackage(typeAliasesPackage); sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); sessionFactory.setPlugins(mybatisSqlInterceptor(),mybatisPlusInterceptor()); sessionFactory.setGlobalConfig(globalConfig()); return sessionFactory.getObject(); } @Bean public MybatisSqlInterceptor mybatisSqlInterceptor() { MybatisSqlInterceptor mybatisSqlInterceptor = new MybatisSqlInterceptor(); Properties properties = new Properties(); mybatisSqlInterceptor.setProperties(properties); return mybatisSqlInterceptor; } /** * 逻辑删除插件 */ @Bean public GlobalConfig globalConfig() { GlobalConfig globalConfig = new GlobalConfig(); GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig(); dbConfig.setLogicDeleteValue("Y"); dbConfig.setLogicNotDeleteValue("N"); globalConfig.setDbConfig(dbConfig); return globalConfig; }
2021年05月11日
2,289 阅读
0 评论
1 点赞
2021-05-11
mybatis plus 出现 Invalid bound statement (not found)
使用mybatis-plus时不能使用自带的SqlSessionFactory,要使用MybatisSqlSessionFactory,在配置类中加入如下配置(springboot) @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { String typeAliasesPackage = env.getProperty("mybatisPlus.typeAliasesPackage"); String mapperLocations = env.getProperty("mybatisPlus.mapperLocations"); String configLocation = env.getProperty("mybatisPlus.configLocation"); typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); VFS.addImplClass(SpringBootVFS.class); final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean (); sessionFactory.setDataSource(dataSource); sessionFactory.setTypeAliasesPackage(typeAliasesPackage); sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); sessionFactory.setPlugins(mybatisSqlInterceptor(),mybatisPlusInterceptor()); return sessionFactory.getObject(); }
2021年05月11日
1,372 阅读
0 评论
2 点赞
2021-05-07
mybatis-plus自动注入sql语句
我们在日常开发中,一般会在表里面增加创建人(create_by)、创建时间(create_time)、修改人(update_by)、修改时间(update_time)四个字段。创建人:新增时,通过上下文获取用户信息。创建时间:新增时,获取系统当前时间。修改人:修改时,通过上下文获取用户信息。修改时间:修改时,获取系统当前时间。对于这种公共的字段,如果每次都在Sql中进行处理,那是相当繁琐也是没必要的。如果你用的是MyBatis-Plus那么一切就变的简单了。继承mybatis的Interceptor拦截器/** * Description: mybatis拦截器,自动注入创建人、创建时间、修改人、修改时间 * * @author : laughing * DateTime: 2021-05-06 17:13 */ @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) @Slf4j @SuppressWarnings("all") public class MybatisSqlInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; String sqlId = mappedStatement.getId(); SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType(); Object parameter = invocation.getArgs()[1]; if (parameter == null) { return invocation.proceed(); } if(SysOperateLog.class.isAssignableFrom(parameter.getClass()) || SysLoginInfo.class.isAssignableFrom(parameter.getClass())){ return invocation.proceed(); } if (SqlCommandType.INSERT == sqlCommandType) { Field[] fields = ConvertUtils.getAllFields(parameter); Long userId = SecurityUtils.getUserId(); for (Field field : fields) { try { if ("createBy".equals(field.getName())) { field.setAccessible(true); Object local_createBy = field.get(parameter); field.setAccessible(false); if (local_createBy == null || local_createBy.equals("")) { // 登录人账号 field.setAccessible(true); field.set(parameter, userId); field.setAccessible(false); } } // 注入创建时间 if ("createTime".equals(field.getName())) { field.setAccessible(true); Object local_createDate = field.get(parameter); field.setAccessible(false); if (local_createDate == null || local_createDate.equals("")) { field.setAccessible(true); field.set(parameter, new Date()); field.setAccessible(false); } } } catch (Exception e) { } } } if (SqlCommandType.UPDATE == sqlCommandType) { Long userId = SecurityUtils.getUserId(); Field[] fields = null; if (parameter instanceof MapperMethod.ParamMap) { MapperMethod.ParamMap<?> p = (MapperMethod.ParamMap<?>) parameter; if (p.containsKey("et")) { parameter = p.get("et"); } else { parameter = p.get("param1"); } if (parameter == null) { return invocation.proceed(); } fields = ConvertUtils.getAllFields(parameter); } else { fields = ConvertUtils.getAllFields(parameter); } for (Field field : fields) { try { if ("updateBy".equals(field.getName())) { //获取登录用户信息 // 登录账号 field.setAccessible(true); field.set(parameter, userId); field.setAccessible(false); } if ("updateTime".equals(field.getName())) { field.setAccessible(true); field.set(parameter, new Date()); field.setAccessible(false); } } catch (Exception e) { e.printStackTrace(); } } } return invocation.proceed(); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { // TODO Auto-generated method stub } }通过SqlSessionFactory加载插件/** * Mybatis-Plus配置 * * @author leeframe */ @Configuration @EnableTransactionManagement public class MyBatisPlusConfig { @javax.annotation.Resource private Environment env; static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; private final Logger logger = LoggerFactory.getLogger(MyBatisPlusConfig.class); @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor // 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false // interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); interceptor.addInnerInterceptor(new TenantLineInnerInterceptor( new TenantLineHandler() { // manager_id = 1088248166370832385 // 获取租户 ID 值表达式,只支持单个 ID 值 @Override public Expression getTenantId() { return new StringValue(SecurityUtils.getOrgCode()); } // 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件, // 这里设置 role表不需要该条件 @Override public boolean ignoreTable(String tableName) { return StringUtils.isEmpty(SecurityUtils.getOrgCode()) || LeeFrameConfig.getFilterTableList().stream().anyMatch(e -> e.equalsIgnoreCase(tableName)); } @Override public String getTenantIdColumn() { return "org_code"; } })); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> configuration.setUseDeprecatedExecutor(false); } public static String setTypeAliasesPackage(String typeAliasesPackage) { ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver(); MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); List<String> allResult = new ArrayList<String>(); try { for (String aliasesPackage : typeAliasesPackage.split(",")) { List<String> result = new ArrayList<String>(); aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN; Resource[] resources = resolver.getResources(aliasesPackage); if (resources != null && resources.length > 0) { MetadataReader metadataReader = null; for (Resource resource : resources) { if (resource.isReadable()) { metadataReader = metadataReaderFactory.getMetadataReader(resource); try { result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } if (result.size() > 0) { HashSet<String> hashResult = new HashSet<String>(result); allResult.addAll(hashResult); } } if (allResult.size() > 0) { typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0])); } else { throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包"); } } catch (IOException e) { e.printStackTrace(); } return typeAliasesPackage; } public Resource[] resolveMapperLocations(String[] mapperLocations) { ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); List<Resource> resources = new ArrayList<Resource>(); if (mapperLocations != null) { for (String mapperLocation : mapperLocations) { try { Resource[] mappers = resourceResolver.getResources(mapperLocation); resources.addAll(Arrays.asList(mappers)); } catch (IOException e) { // ignore } } } return resources.toArray(new Resource[resources.size()]); } @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { String typeAliasesPackage = env.getProperty("mybatisPlus.typeAliasesPackage"); String mapperLocations = env.getProperty("mybatisPlus.mapperLocations"); String configLocation = env.getProperty("mybatisPlus.configLocation"); typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); VFS.addImplClass(SpringBootVFS.class); final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setTypeAliasesPackage(typeAliasesPackage); sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); sessionFactory.setPlugins(mybatisSqlInterceptor()); return sessionFactory.getObject(); } @Bean public MybatisSqlInterceptor mybatisSqlInterceptor() { MybatisSqlInterceptor mybatisSqlInterceptor = new MybatisSqlInterceptor(); Properties properties = new Properties(); mybatisSqlInterceptor.setProperties(properties); return mybatisSqlInterceptor; } }
2021年05月07日
1,417 阅读
0 评论
0 点赞
2021-04-24
Spring Boot 2.0+ ByBatis-Plus动态切换数据源
前端时间做了一个SaaS的应用,也是第一次弄SaaS相关软件。系统设计的不同租户之前是数据库级别的隔离。所以核心的内容基本就是如何实现不同之间的数据源的切换。创建模拟数据库创建两个数据库,分别命名为dynamic_datasource_one和dynamic_datasource_two,两个数据库内分别创建两张表,分别名称为table_one和table_two。数据库名表名dynamic_datasource_onetable_onedynamic_datasource_twotable_two创建表的Sql如下:create table table_one ( id bigint auto_increment primary key, name varchar(30) null ); INSERT INTO table_one (id, name) VALUES (1, 'dynamic_datasource_one'); create table table_two ( id bigint auto_increment primary key, name varchar(30) null ); INSERT INTO table_two (id, name) VALUES (1, 'dynamic_datasource_two');创建工程创建工程就不多说了,主要是添加mybatis-plus依赖就好了。 <!-- 添加MyBatis-plus依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>3.4.1</version> </dependency>修改配置文件配置两个数据库的连接信息,我这里放到配置文件中,如果是多租户的,也可以从数据库里面获取数据库连接信息。spring: datasource: url: jdbc:mysql://localhost:3306/dynamic_datasource_one?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: type-aliases-package: net.xiangcaowuyu.DynamicDataSource.doamin mapper-locations: classpath:mapper/*Mapper.xml添加配置文件将yaml中的配置信息映射到类上,方便使用。创建MyBatisConfig.java@Configuration @EnableTransactionManagement public class MyBatisConfig { private final Logger logger = LoggerFactory.getLogger(MyBatisConfig.class); /** * 数据源1 * @return */ public DataSource dynamicDataSourceOne( ) { return DataSourceBuilder .create() .url("jdbc:mysql://localhost:3306/dynamic_datasource_one?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true") .username("root") .password("123456") .driverClassName("com.mysql.jdbc.Driver") .build(); } /** * 数据源2 * @return */ public DataSource dynamicDataSourceTwo() { return DataSourceBuilder .create() .url("jdbc:mysql://localhost:3306/dynamic_datasource_two?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true") .username("root") .password("123456") .driverClassName("com.mysql.jdbc.Driver") .build(); } @Bean("dynamicDataSource") public DataSource dynamicDataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); DataSource dynamicDataSourceOne = dynamicDataSourceOne(); DataSource dynamicDataSourceTwo = dynamicDataSourceTwo(); Map<Object,Object> dynamicDataSourceMap = new HashMap<>(); dynamicDataSourceMap.put("dynamicDataSourceOne",dynamicDataSourceOne); dynamicDataSourceMap.put("dynamicDataSourceTwo",dynamicDataSourceTwo); DynamicDataSourceContextHolder.addDataSourceKeys(dynamicDataSourceMap.keySet()); dynamicDataSource.setTargetDataSources(dynamicDataSourceMap); //必须执行此操作,才会重新初始化AbstractRoutingDataSource 中的 resolvedDataSources,也只有这样,动态切换才会起效 dynamicDataSource.afterPropertiesSet(); return dynamicDataSource; } }创建DataSourceContextHolder存储当前数据源public class DynamicDataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(){ @Override protected String initialValue(){ return "dynamicDataSourceOne"; } }; public static List<Object> dataSourceKeyList = new ArrayList<>(); public static void setDataSourceKey(String key) throws Exception { if(!dataSourceKeyList.contains(key)){ throw new Exception("数据库连接不存在"); } contextHolder.set(key); } public static String getDataSourceKey(){ return contextHolder.get(); } public static void clearDataSourceKey(String key){ contextHolder.remove(); } /** * 添加数据库连接键 * @param keys */ public static void addDataSourceKeys(Collection<Object> keys){ dataSourceKeyList.addAll(keys); } }创建DynamicDataSource配置每次执行sql时,调用的数据源。/** * 动态数据源 */ public class DynamicDataSource extends AbstractRoutingDataSource { /** * 设置数据源Key值来切换数据,每次执行sql会先调用这个方法 * @return */ @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceKey(); } }测试创建MapperTableOneMapper@Mapper public interface TableOneMapper extends BaseMapper<TableOne> { }TableTwoMapper@Mapper public interface TableTwoMapper extends BaseMapper<TableOne> { }创建controller@RestController @RequestMapping("/") public class DynamicDatSourceController { private final Logger logger = LoggerFactory.getLogger(DynamicDatSourceController.class); @Resource TableOneMapper tableOneMapper; @Resource TableTwoMapper tableTwoMapper; @GetMapping public void test() throws Exception { logger.info("准备查询数据源1的数据"); logger.info(tableOneMapper.selectList(new QueryWrapper<>()).get(0).getName()); logger.info("准备查询数据源2的数据"); DynamicDataSourceContextHolder.setDataSourceKey("dynamicDataSourceTwo"); logger.info(tableTwoMapper.selectList(new QueryWrapper<>()).get(0).getName()); } }
2021年04月24日
1,209 阅读
0 评论
0 点赞
2021-04-22
mybatis-plus基于p6spy执行 SQL 分析打印
书接上文,MyBatis-Plus简易使用教程该功能依赖 p6spy 组件,完美的输出打印 SQL 及执行时长 3.1.0 以上版本。添加p6spy依赖<!--输出打印sql--> <dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>3.8.7</version> </dependency>配置数据库驱动spring: datasource: url: jdbc:p6spy:mysql://localhost:3306/mybatisplus username: root password: 123456 driver-class-name: com.p6spy.engine.spy.P6SpyDriver配置p6spy在resources文件夹下增加spy.properties文件,内容如下#3.2.1以上使用 modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory #3.2.1以下使用或者不配置 #modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory # 自定义日志打印 logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger #日志输出到控制台 appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger # 使用日志系统记录 sql #appender=com.p6spy.engine.spy.appender.Slf4JLogger # 设置 p6spy driver 代理 deregisterdrivers=true # 取消JDBC URL前缀 useprefix=true # 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset. excludecategories=info,debug,result,commit,resultset # 日期格式 dateformat=yyyy-MM-dd HH:mm:ss # 实际驱动可多个 #driverlist=org.h2.Driver # 是否开启慢SQL记录 outagedetection=true # 慢SQL记录标准 2 秒 outagedetectioninterval=2测试重启应用,在控制台可以查看执行的sql信息
2021年04月22日
1,486 阅读
0 评论
0 点赞
2021-04-22
mybatis-plus基于行级别的多租户实现
什么是多租户多租户技术或称多重租赁技术,简称SaaS,是一种软件架构技术,是实现如何在多用户环境下(多用户一般是面向企业用户)共用相同的系统或程序组件,并且可确保各用户间数据的隔离性。简单讲:在一台服务器上运行单个应用实例,它为多个租户(客户)提供服务。从定义中我们可以理解:多租户是一种架构,目的是为了让多用户环境下使用同一套程序,且保证用户间数据隔离。那么重点就很浅显易懂了,多租户的重点就是同一套程序下实现多用户数据的隔离。多租户架构以及数据隔离方案方案一:独立数据库即一个租户一个数据库,这种方案的用户数据隔离级别最高,安全性最好,但成本较高。优点:为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租户的独特需求;如果出现故障,恢复数据比较简单。缺点:增多了数据库的安装数量,随之带来维护成本和购置成本的增加。方案二:共享数据库,独立 Schema也就是说 共同使用一个数据库 使用表进行数据隔离多个或所有租户共享Database,但是每个租户一个Schema(也可叫做一个user)。底层库比如是:DB2、ORACLE等,一个数据库下可以有多个SCHEMA。优点:为安全性要求较高的租户提供了一定程度的逻辑数据隔离,并不是完全隔离;每个数据库可支持更多的租户数量。缺点:如果出现故障,数据恢复比较困难,因为恢复数据库将牵涉到其他租户的数据;方案三:共享数据库,共享 Schema,共享数据表也就是说 共同使用一个数据库一个表 使用字段进行数据隔离即租户共享同一个Database、同一个Schema,但在表中增加TenantID多租户的数据字段。这是共享程度最高、隔离级别最低的模式。简单来讲,即每插入一条数据时都需要有一个客户的标识。这样才能在同一张表中区分出不同客户的数据,这也是我们系统目前用到的(tenant_id)优点:三种方案比较,第三种方案的维护和购置成本最低,允许每个数据库支持的租户数量最多。缺点:隔离级别最低,安全性最低,需要在设计开发时加大对安全的开发量;数据备份和恢复最困难,需要逐表逐条备份和还原。基于行级别的隔离本文基于方案三:共享数据库,共享 Schema,共享数据表进行说明。关于MyBatis-Plus的基础配置,可以参考MyBatis-Plus简易使用教程使用表结构create table user ( id bigint auto_increment comment '主键ID' primary key, name varchar(30) null comment '姓名', age int null comment '年龄', email varchar(50) null comment '邮箱', del_flag char default 'N' null, tenant_id bigint null comment '租户' );预置数据INSERT INTO user (id, name, age, email, del_flag, tenant_id) VALUES (1, 'Jone', 18, 'test1@baomidou.com', 'Y', 1); INSERT INTO user (id, name, age, email, del_flag, tenant_id) VALUES (2, 'Jack', 20, 'test2@baomidou.com', 'N', 2); INSERT INTO user (id, name, age, email, del_flag, tenant_id) VALUES (3, 'Tom', 28, 'test3@baomidou.com', 'N', 1); INSERT INTO user (id, name, age, email, del_flag, tenant_id) VALUES (4, 'Sandy', 21, 'test4@baomidou.com', 'N', 2); INSERT INTO user (id, name, age, email, del_flag, tenant_id) VALUES (5, 'Billie', 24, 'test5@baomidou.com', 'N', 1);mybatis-plus配置在mybatis-plus配置类中,定义拦截器,并加入租户相关拦截器TenantLineInnerInterceptor@Configuration public class MybatisPlusConfig { // 最新版 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); TenantLineHandler tenantLineHandler = new TenantLineHandler() { @Override public Expression getTenantId() { return new LongValue(1); } @Override public String getTenantIdColumn() { return "tenant_id"; } @Override public boolean ignoreTable(String tableName) { return false; } }; interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(tenantLineHandler)); return interceptor; } @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> configuration.setUseDeprecatedExecutor(false); } }测试1.基于mybatis-plus的CRUD操作@GetMapping("list") public List<User> list(){ return userService.list(); }2.自定义sql测试UserMapper.java/** * 租户的查询 * @return */ List<User> selectUserTenant();UserMapper.xml<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="net.xiangcaowuyu.mybatisplussample.mapper.UserMapper"> <resultMap id="BaseResultMap" type="net.xiangcaowuyu.mybatisplussample.domain.User"> <!--@Table `user`--> <id column="id" jdbcType="BIGINT" property="id"/> <result column="name" jdbcType="VARCHAR" property="name"/> <result column="age" jdbcType="INTEGER" property="age"/> <result column="email" jdbcType="VARCHAR" property="email"/> </resultMap> <sql id="Base_Column_List"> id, `name`, age, email </sql> <select id="selectUserList" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from user </select> <select id="selectUserTenant" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from user </select> </mapper>@GetMapping(value = "listTenant") public List<User> listTenant() { return userService.selectUserTenant(); }3.增加数据@GetMapping("add") public Boolean addUser() { User user = new User(); user.setName("张三"); user.setAge(30); return userService.save(user); }
2021年04月22日
1,610 阅读
0 评论
0 点赞
2021-04-22
MyBatis-Plus逻辑删除
逻辑删除::: tip 说明:只对自动注入的sql起效:插入: 不作限制查找: 追加where条件过滤掉已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段更新: 追加where条件防止更新到已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段删除: 转变为 更新例如:删除: update user set deleted=1 where id = 1 and deleted=0查找: select id,name,deleted from user where deleted=0字段类型支持说明:支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime)如果数据库字段使用datetime,逻辑未删除值和已删除值支持配置为字符串null,另一个值支持配置为函数来获取值如now()附录:逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。使用方法:步骤1: MySql表结构create table if not exists user ( id bigint not null comment '主键ID' primary key, name varchar(30) null comment '姓名', age int null comment '年龄', email varchar(50) null comment '邮箱', del_flag char default 'N' null );步骤2: 预置数据INSERT INTO user (id, name, age, email, del_flag) VALUES (1, 'Jone', 18, 'test1@baomidou.com', 'Y'); INSERT INTO user (id, name, age, email, del_flag) VALUES (2, 'Jack', 20, 'test2@baomidou.com', 'N'); INSERT INTO user (id, name, age, email, del_flag) VALUES (3, 'Tom', 28, 'test3@baomidou.com', 'N'); INSERT INTO user (id, name, age, email, del_flag) VALUES (4, 'Sandy', 21, 'test4@baomidou.com', 'N'); INSERT INTO user (id, name, age, email, del_flag) VALUES (5, 'Billie', 24, 'test5@baomidou.com', 'N');步骤3: 配置com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig例: application.ymlmybatis-plus: global-config: db-config: logic-delete-field: del_flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2) logic-delete-value: 'Y' # 逻辑已删除值(默认为 1) logic-not-delete-value: 'N' # 逻辑未删除值(默认为 0)步骤4: 实体类字段上加上@TableLogic注解@Data @TableName(value = "`user`") public class User implements Serializable { /** * 主键ID */ @TableId(value = "id", type = IdType.AUTO) private Long id; /** * 姓名 */ @TableField(value = "`name`") private String name; /** * 年龄 */ @TableField(value = "age") private Integer age; /** * 邮箱 */ @TableField(value = "email") private String email; /** * 逻辑删除字段 */ @TableField(value = "del_flag") @TableLogic private String delFlag; private static final long serialVersionUID = 1L; public static final String COL_ID = "id"; public static final String COL_NAME = "name"; public static final String COL_AGE = "age"; public static final String COL_EMAIL = "email"; }步骤4: 测试@GetMapping("logicdelete/{id}") public Boolean logicdelete(@PathVariable("id") Long id){ return userService.removeById(id); }常见问题:1. 如何 insert ?字段在数据库定义默认值(推荐)insert 前自己 set 值使用自动填充功能2. 删除接口自动填充功能失效使用 update 方法并: UpdateWrapper.set(column, value)(推荐)使用 update 方法并: UpdateWrapper.setSql("column=value")使用Sql注入器注入com.baomidou.mybatisplus.extension.injector.methods.LogicDeleteByIdWithFill并使用(推荐)
2021年04月22日
2,774 阅读
0 评论
1 点赞
2021-04-22
MyBatis-Plus简易使用教程
添加依赖添加MyBatis-plus依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency>配置初始化数据库演示SQL脚本如下CREATE TABLE user ( id BIGINT(20) NOT NULL COMMENT '主键ID', name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名', age INT(11) NULL DEFAULT NULL COMMENT '年龄', email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (id) )INSERT INTO user (id, name, age, email) VALUES (1, 'Jone', 18, 'test1@baomidou.com'), (2, 'Jack', 20, 'test2@baomidou.com'), (3, 'Tom', 28, 'test3@baomidou.com'), (4, 'Sandy', 21, 'test4@baomidou.com'), (5, 'Billie', 24, 'test5@baomidou.com');配置数据库连接在 application.yml 配置文件中添加 MySql 数据库的相关配置。spring: datasource: url: jdbc:mysql://localhost:3306/mybatisplus username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹@SpringBootApplication @MapperScan("net.xiangcaowuyu.mybatisplussample.mapper") public class MyBatisPlusSampleApplication { public static void main(String[] args) { SpringApplication.run(MyBatisPlusSampleApplication.class, args); } }快速开始编写实体类 User.java@Data @TableName(value = "`user`") public class User implements Serializable { /** * 主键ID */ @TableId(value = "id", type = IdType.AUTO) private Long id; /** * 姓名 */ @TableField(value = "`name`") private String name; /** * 年龄 */ @TableField(value = "age") private Integer age; /** * 邮箱 */ @TableField(value = "email") private String email; private static final long serialVersionUID = 1L; public static final String COL_ID = "id"; public static final String COL_NAME = "name"; public static final String COL_AGE = "age"; public static final String COL_EMAIL = "email"; }编写mapper类UserMapper.javapublic interface UserMapper extends BaseMapper<User> { }编写service类IUserService.javapublic interface IUserService extends IService<User>{ }编写impl类UserServiceImpl.java@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService { }编写controller类UserController.java@RestController @RequestMapping("user") public class UserController { @Resource private IUserService userService; @GetMapping("list") public List<User> list(){ return userService.list(); } }MyBatis-Plus常见注解@TableName描述:表名注解属性类型必须指定默认值描述valueString否""表名schemaString否""schemakeepGlobalPrefixboolean否false是否保持使用全局的 tablePrefix 的值(如果设置了全局 tablePrefix 且自行设置了 value 的值)resultMapString否""xml 中 resultMap 的 idautoResultMapboolean否false是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建并注入)excludePropertyString[]否{}需要排除的属性名(@since 3.3.1)@TableId描述:主键注解属性类型必须指定默认值描述valueString否""主键字段名typeEnum否IdType.NONE主键类型IdType值描述AUTO数据库ID自增NONE无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)INPUTinsert前自行set主键值ASSIGN_ID分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)ASSIGN_UUID分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认default方法)ID_WORKER分布式全局唯一ID 长整型类型(please use ASSIGN_ID)UUID32位UUID字符串(please use ASSIGN_UUID)ID_WORKER_STR分布式全局唯一ID 字符串类型(please use ASSIGN_ID)@TableField描述:字段注解(非主键)属性类型必须指定默认值描述valueString否""数据库字段名elString否""映射为原生 #{ ... } 逻辑,相当于写在 xml 里的 #{ ... } 部分existboolean否true是否为数据库表字段@TableLogic描述:表字段逻辑处理注解(逻辑删除)属性类型必须指定默认值描述valueString否""逻辑未删除值delvalString否""逻辑删除值CRUD接口Service CRUD 接口说明:通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,泛型 T 为任意实体对象建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承 Mybatis-Plus 提供的基类对象 Wrapper 为 条件构造器Save// 插入一条记录(选择字段,策略插入) boolean save(T entity); // 插入(批量) boolean saveBatch(Collection<T> entityList); // 插入(批量) boolean saveBatch(Collection<T> entityList, int batchSize);参数说明类型参数名描述Tentity实体对象CollectionentityList实体对象集合intbatchSize插入批次数量SaveOrUpdate// TableId 注解存在更新记录,否插入一条记录 boolean saveOrUpdate(T entity); // 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法 boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper); // 批量修改插入 boolean saveOrUpdateBatch(Collection<T> entityList); // 批量修改插入 boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);参数说明类型参数名描述Tentity实体对象WrapperupdateWrapper实体对象封装操作类 UpdateWrapperCollectionentityList实体对象集合intbatchSize插入批次数量Remove// 根据 entity 条件,删除记录 boolean remove(Wrapper<T> queryWrapper); // 根据 ID 删除 boolean removeById(Serializable id); // 根据 columnMap 条件,删除记录 boolean removeByMap(Map<String, Object> columnMap); // 删除(根据ID 批量删除) boolean removeByIds(Collection<? extends Serializable> idList);参数说明类型参数名描述WrapperqueryWrapper实体包装类 QueryWrapperSerializableid主键IDMap<String, Object>columnMap表字段 map 对象Collection<? extends Serializable>idList主键ID列表Update// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset boolean update(Wrapper<T> updateWrapper); // 根据 whereEntity 条件,更新记录 boolean update(T entity, Wrapper<T> updateWrapper); // 根据 ID 选择修改 boolean updateById(T entity); // 根据ID 批量更新 boolean updateBatchById(Collection<T> entityList); // 根据ID 批量更新 boolean updateBatchById(Collection<T> entityList, int batchSize);参数说明类型参数名描述WrapperupdateWrapper实体对象封装操作类 UpdateWrapperTentity实体对象CollectionentityList实体对象集合intbatchSize更新批次数量Get// 根据 ID 查询 T getById(Serializable id); // 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1") T getOne(Wrapper<T> queryWrapper); // 根据 Wrapper,查询一条记录 T getOne(Wrapper<T> queryWrapper, boolean throwEx); // 根据 Wrapper,查询一条记录 Map<String, Object> getMap(Wrapper<T> queryWrapper); // 根据 Wrapper,查询一条记录 <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);参数说明类型参数名描述Serializableid主键IDWrapperqueryWrapper实体对象封装操作类 QueryWrapperbooleanthrowEx有多个 result 是否抛出异常Tentity实体对象Function<? super Object, V>mapper转换函数List// 查询所有 List<T> list(); // 查询列表 List<T> list(Wrapper<T> queryWrapper); // 查询(根据ID 批量查询) Collection<T> listByIds(Collection<? extends Serializable> idList); // 查询(根据 columnMap 条件) Collection<T> listByMap(Map<String, Object> columnMap); // 查询所有列表 List<Map<String, Object>> listMaps(); // 查询列表 List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper); // 查询全部记录 List<Object> listObjs(); // 查询全部记录 <V> List<V> listObjs(Function<? super Object, V> mapper); // 根据 Wrapper 条件,查询全部记录 List<Object> listObjs(Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询全部记录 <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);参数说明类型参数名描述WrapperqueryWrapper实体对象封装操作类 QueryWrapperCollection<? extends Serializable>idList主键ID列表Map<?String, Object>columnMap表字段 map 对象Function<? super Object, V>mapper转换函数Page// 无条件分页查询 IPage<T> page(IPage<T> page); // 条件分页查询 IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper); // 无条件分页查询 IPage<Map<String, Object>> pageMaps(IPage<T> page); // 条件分页查询 IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);参数说明类型参数名描述IPagepage翻页对象WrapperqueryWrapper实体对象封装操作类 QueryWrapperCount// 查询总记录数 int count(); // 根据 Wrapper 条件,查询总记录数 int count(Wrapper<T> queryWrapper);参数说明类型参数名描述WrapperqueryWrapper实体对象封装操作类 QueryWrapperChainquery// 链式查询 普通 QueryChainWrapper<T> query(); // 链式查询 lambda 式。注意:不支持 Kotlin LambdaQueryChainWrapper<T> lambdaQuery(); // 示例: query().eq("column", value).one(); lambdaQuery().eq(Entity::getId, value).list();update// 链式更改 普通 UpdateChainWrapper<T> update(); // 链式更改 lambda 式。注意:不支持 Kotlin LambdaUpdateChainWrapper<T> lambdaUpdate(); // 示例: update().eq("column", value).remove(); lambdaUpdate().eq(Entity::getId, value).update(entity);Mapper CRUD 接口说明:通用 CRUD 封装BaseMapper接口,为 Mybatis-Plus 启动时自动解析实体表关系映射转换为 Mybatis 内部对象注入容器泛型 T 为任意实体对象参数 Serializable 为任意类型主键 Mybatis-Plus 不推荐使用复合主键约定每一张表都有自己的唯一 id 主键对象 Wrapper 为 条件构造器Insert// 插入一条记录 int insert(T entity);参数说明类型参数名描述Tentity实体对象Delete// 根据 entity 条件,删除记录 int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); // 删除(根据ID 批量删除) int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根据 ID 删除 int deleteById(Serializable id); // 根据 columnMap 条件,删除记录 int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);参数说明类型参数名描述Wrapperwrapper实体对象封装操作类(可以为 null)Collection<? extends Serializable>idList主键ID列表(不能为 null 以及 empty)Serializableid主键IDMap<String, Object>columnMap表字段 map 对象Update// 根据 whereEntity 条件,更新记录 int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper); // 根据 ID 修改 int updateById(@Param(Constants.ENTITY) T entity);参数说明类型参数名描述Tentity实体对象 (set 条件值,可为 null)WrapperupdateWrapper实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)Select// 根据 ID 查询 T selectById(Serializable id); // 根据 entity 条件,查询一条记录 T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询(根据ID 批量查询) List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根据 entity 条件,查询全部记录 List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询(根据 columnMap 条件) List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); // 根据 Wrapper 条件,查询全部记录 List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值 List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 entity 条件,查询全部记录(并翻页) IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询全部记录(并翻页) IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询总记录数 Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);参数说明类型参数名描述Serializableid主键IDWrapperqueryWrapper实体对象封装操作类(可以为 null)Collection<? extends Serializable>idList主键ID列表(不能为 null 以及 empty)Map<String, Object>columnMap表字段 map 对象IPagepage分页查询条件(可以为 RowBounds.DEFAULT)mapper 层 选装件说明:选装件位于 com.baomidou.mybatisplus.extension.injector.methods 包下 需要配合Sql 注入器使用,案例(opens new window)使用详细见AlwaysUpdateSomeColumnByIdint alwaysUpdateSomeColumnById(T entity);insertBatchSomeColumnint insertBatchSomeColumn(List<T> entityList);deleteByIdWithFillint deleteByIdWithFill(T entity);条件构造器说明:以下出现的第一个入参boolean condition表示该条件是否加入最后生成的sql中,例如:query.like(StringUtils.isNotBlank(name), Entity::getName, name) .eq(age!=null && age >= 0, Entity::getAge, age)以下代码块内的多个方法均为从上往下补全个别boolean类型的入参,默认为true以下出现的泛型Param均为Wrapper的子类实例(均具有AbstractWrapper的所有方法)以下方法在入参中出现的R为泛型,在普通wrapper中是String,在LambdaWrapper中是函数(例:Entity::getId,Entity为实体类,getId为字段id的getMethod)以下方法入参中的R column均表示数据库字段,当R具体类型为String时则为数据库字段名(字段名是数据库关键字的自己用转义符包裹!)。而不是实体类数据字段名!,另当R具体类型为SFunction时项目runtime不支持eclipse自家的编译器。以下举例均为使用普通wrapper,入参为Map和List的均以json形式表现!使用中如果入参的Map或者List为空,则不会加入最后生成的sql中!!!警告:不支持以及不赞成在 RPC 调用中把 Wrapper 进行传输wrapper 很重传输 wrapper 可以类比为你的 controller 用 map 接收值(开发一时爽,维护火葬场)正确的 RPC 调用姿势是写一个 DTO 进行传输,被调用方再根据 DTO 执行相应的操作AbstractWrapper说明:QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类,用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件。注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为allEqallEq(Map<R, V> params) allEq(Map<R, V> params, boolean null2IsNull) allEq(boolean condition, Map<R, V> params, boolean null2IsNull)全部eq或个别isNull个别参数说明:params : key为数据库字段名,value为字段值null2IsNull : 为true则在map的value为null时调用 isNull方法,为false时则忽略value为null的例1: allEq({id:1,name:"老王",age:null})--->id = 1 and name = '老王' and age is null例2: allEq({id:1,name:"老王",age:null}, false)--->id = 1 and name = '老王'allEq(BiPredicate<R, V> filter, Map<R, V> params) allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull) allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull) 个别参数说明:filter : 过滤函数,是否允许字段传入比对条件中params 与 null2IsNull : 同上例1: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null})--->name = '老王' and age is null例2: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"老王",age:null}, false)--->name = '老王'eqeq(R column, Object val) eq(boolean condition, R column, Object val)等于 =例: eq("name", "老王")--->name = '老王'nene(R column, Object val) ne(boolean condition, R column, Object val)不等于 <>例: ne("name", "老王")--->name <> '老王'gtgt(R column, Object val) gt(boolean condition, R column, Object val)大于 >例: gt("age", 18)--->age > 18gege(R column, Object val) ge(boolean condition, R column, Object val)大于等于 >=例: ge("age", 18)--->age >= 18ltlt(R column, Object val) lt(boolean condition, R column, Object val)小于 <例: lt("age", 18)--->age < 18lele(R column, Object val) le(boolean condition, R column, Object val)小于等于 <=例: le("age", 18)--->age <= 18betweenbetween(R column, Object val1, Object val2) between(boolean condition, R column, Object val1, Object val2)BETWEEN 值1 AND 值2例: between("age", 18, 30)--->age between 18 and 30notBetweennotBetween(R column, Object val1, Object val2) notBetween(boolean condition, R column, Object val1, Object val2)NOT BETWEEN 值1 AND 值2例: notBetween("age", 18, 30)--->age not between 18 and 30likelike(R column, Object val) like(boolean condition, R column, Object val)LIKE '%值%'例: like("name", "王")--->name like '%王%'notLikenotLike(R column, Object val) notLike(boolean condition, R column, Object val)NOT LIKE '%值%'例: notLike("name", "王")--->name not like '%王%'likeLeftlikeLeft(R column, Object val) likeLeft(boolean condition, R column, Object val)LIKE '%值'例: likeLeft("name", "王")--->name like '%王'likeRightlikeRight(R column, Object val) likeRight(boolean condition, R column, Object val)LIKE '值%'例: likeRight("name", "王")--->name like '王%'isNullisNull(R column) isNull(boolean condition, R column)字段 IS NULL例: isNull("name")--->name is nullisNotNullisNotNull(R column) isNotNull(boolean condition, R column)字段 IS NOT NULL例: isNotNull("name")--->name is not nullinin(R column, Collection<?> value) in(boolean condition, R column, Collection<?> value)字段 IN (value.get(0), value.get(1), ...)例: in("age",{1,2,3})--->age in (1,2,3)in(R column, Object... values) in(boolean condition, R column, Object... values)字段 IN (v0, v1, ...)例: in("age", 1, 2, 3)--->age in (1,2,3)notInnotIn(R column, Collection<?> value) notIn(boolean condition, R column, Collection<?> value)字段 NOT IN (value.get(0), value.get(1), ...)例: notIn("age",{1,2,3})--->age not in (1,2,3)notIn(R column, Object... values) notIn(boolean condition, R column, Object... values)字段 NOT IN (v0, v1, ...)例: notIn("age", 1, 2, 3)--->age not in (1,2,3)inSqlinSql(R column, String inValue) inSql(boolean condition, R column, String inValue)字段 IN ( sql语句 )例: inSql("age", "1,2,3,4,5,6")--->age in (1,2,3,4,5,6)例: inSql("id", "select id from table where id < 3")--->id in (select id from table where id < 3)notInSqlnotInSql(R column, String inValue) notInSql(boolean condition, R column, String inValue)字段 NOT IN ( sql语句 )例: notInSql("age", "1,2,3,4,5,6")--->age not in (1,2,3,4,5,6)例: notInSql("id", "select id from table where id < 3")--->id not in (select id from table where id < 3)groupBygroupBy(R... columns) groupBy(boolean condition, R... columns)分组:GROUP BY 字段, ...例: groupBy("id", "name")--->group by id,nameorderByAscorderByAsc(R... columns) orderByAsc(boolean condition, R... columns)排序:ORDER BY 字段, ... ASC例: orderByAsc("id", "name")--->order by id ASC,name ASCorderByDescorderByDesc(R... columns) orderByDesc(boolean condition, R... columns)排序:ORDER BY 字段, ... DESC例: orderByDesc("id", "name")--->order by id DESC,name DESCorderByorderBy(boolean condition, boolean isAsc, R... columns)排序:ORDER BY 字段, ...例: orderBy(true, true, "id", "name")--->order by id ASC,name ASChavinghaving(String sqlHaving, Object... params) having(boolean condition, String sqlHaving, Object... params)HAVING ( sql语句 )例: having("sum(age) > 10")--->having sum(age) > 10例: having("sum(age) > {0}", 11)--->having sum(age) > 11funcfunc(Consumer<Children> consumer) func(boolean condition, Consumer<Children> consumer)func 方法(主要方便在出现if...else下调用不同方法能不断链)例: func(i -> if(true) {i.eq("id", 1)} else {i.ne("id", 1)})oror() or(boolean condition)拼接 OR注意事项:主动调用or表示紧接着下一个方法不是用and连接!(不调用or则默认为使用and连接)例: eq("id",1).or().eq("name","老王")--->id = 1 or name = '老王'or(Consumer<Param> consumer) or(boolean condition, Consumer<Param> consumer)OR 嵌套例: or(i -> i.eq("name", "李白").ne("status", "活着"))--->or (name = '李白' and status <> '活着')andand(Consumer<Param> consumer) and(boolean condition, Consumer<Param> consumer)AND 嵌套例: and(i -> i.eq("name", "李白").ne("status", "活着"))--->and (name = '李白' and status <> '活着')nestednested(Consumer<Param> consumer) nested(boolean condition, Consumer<Param> consumer)正常嵌套 不带 AND 或者 OR例: nested(i -> i.eq("name", "李白").ne("status", "活着"))--->(name = '李白' and status <> '活着')applyapply(String applySql, Object... params) apply(boolean condition, String applySql, Object... params)拼接 sql注意事项:该方法可用于数据库函数 动态入参的params对应前面applySql内部的{index}部分.这样是不会有sql注入风险的,反之会有!例: apply("id = 1")--->id = 1例: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")例: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")lastlast(String lastSql) last(boolean condition, String lastSql)无视优化规则直接拼接到 sql 的最后注意事项:只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用例: last("limit 1")existsexists(String existsSql) exists(boolean condition, String existsSql)拼接 EXISTS ( sql语句 )例: exists("select id from table where age = 1")--->exists (select id from table where age = 1)notExistsnotExists(String notExistsSql) notExists(boolean condition, String notExistsSql)拼接 NOT EXISTS ( sql语句 )例: notExists("select id from table where age = 1")--->not exists (select id from table where age = 1)QueryWrapper说明:继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件及 LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取selectselect(String... sqlSelect) select(Predicate<TableFieldInfo> predicate) select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)设置查询字段说明:以上方法分为两类.第二类方法为:过滤查询字段(主键除外),入参不包含 class 的调用前需要wrapper内的entity属性有值! 这两类方法重复调用以最后一次为准例: select("id", "name", "age")例: select(i -> i.getProperty().startsWith("test"))UpdateWrapper说明:继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件及 LambdaUpdateWrapper, 可以通过 new UpdateWrapper().lambda() 方法获取!setset(String column, Object val) set(boolean condition, String column, Object val)SQL SET 字段例: set("name", "老李头")例: set("name", "")--->数据库字段值变为空字符串例: set("name", null)--->数据库字段值变为nullsetSqlsetSql(String sql)设置 SET 部分 SQL例: setSql("name = '老李头'")lambda获取 LambdaWrapper在QueryWrapper中是获取LambdaQueryWrapper在UpdateWrapper中是获取LambdaUpdateWrapper使用 Wrapper 自定义SQL注意事项:需要mybatis-plus版本 >= 3.0.7 param 参数名要么叫ew,要么加上注解@Param(Constants.WRAPPER) 使用${ew.customSqlSegment} 不支持 Wrapper 内的entity生成where语句用注解@Select("select * from mysql_data ${ew.customSqlSegment}") List<MysqlData> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);用XMLList<MysqlData> getAll(Wrapper ew); <select id="getAll" resultType="MysqlData"> SELECT * FROM mysql_data ${ew.customSqlSegment} </select>链式调用 lambda 式// 区分: // 链式调用 普通 UpdateChainWrapper<T> update(); // 链式调用 lambda 式。注意:不支持 Kotlin LambdaUpdateChainWrapper<T> lambdaUpdate(); // 等价示例: query().eq("id", value).one(); lambdaQuery().eq(Entity::getId, value).one(); // 等价示例: update().eq("id", value).remove(); lambdaUpdate().eq(Entity::getId, value).remove();分页插件配置依赖 <!-- 添加MyBatis-plus依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>3.4.1</version> </dependency>增加配置文件@Configuration public class MybatisPlusConfig { // 最新版 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> configuration.setUseDeprecatedExecutor(false); } }编写mapper类@Mapper public interface UserMapper extends BaseMapper<User> { /** * 分页查询 * * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位(你可以继承Page实现自己的分页对象) * @return */ Page<User> selectUserList(IPage<User> page); }xml编写Sql<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="net.xiangcaowuyu.mybatisplussample.mapper.UserMapper"> <resultMap id="BaseResultMap" type="net.xiangcaowuyu.mybatisplussample.domain.User"> <!--@Table `user`--> <id column="id" jdbcType="BIGINT" property="id"/> <result column="name" jdbcType="VARCHAR" property="name"/> <result column="age" jdbcType="INTEGER" property="age"/> <result column="email" jdbcType="VARCHAR" property="email"/> </resultMap> <sql id="Base_Column_List"> id, `name`, age, email </sql> <select id="selectUserList" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from user </select> </mapper>编写service类public interface IUserService extends IService<User>{ /** * 分页查询 * * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位(你可以继承Page实现自己的分页对象) * @return */ Page<User> selectUserList(IPage<User> page); }编写impl类@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService { @Resource UserMapper userMapper; /** * 分页查询 * * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位(你可以继承Page实现自己的分页对象) * @return */ @Override public Page<User> selectUserList(IPage<User> page) { return userMapper.selectUserList(page); } }编写controller类@RestController @RequestMapping("user") public class UserController { @Resource private IUserService userService; @GetMapping("list") public List<User> list(){ return userService.list(); } @GetMapping("list1") public List<User> list1(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("id",1).or().eq("name","Tom"); return userService.list(queryWrapper); } @GetMapping("page") public Page<User> page(){ IPage<User> userPage = new Page<>(1,3); return userService.selectUserList(userPage); } }
2021年04月22日
1,180 阅读
0 评论
1 点赞
1
2