SpringBoot+MyBatisPlus:分页查询压缩文件导出大数据量解决方案
Posted 一如既往的热爱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot+MyBatisPlus:分页查询压缩文件导出大数据量解决方案相关的知识,希望对你有一定的参考价值。
在SpringBoot中,可以使用MyBatis-Plus的流式查询功能实现大数据量的导出,并通过压缩文件的方式减小文件大小,提升导出效率。
具体实现步骤如下:
-
配置MyBatis-Plus的分页插件和流式查询插件。
-
创建一个Controller方法,接收客户端的请求,并使用流式查询功能查询出需要导出的数据。
-
将查询出的数据写入到Excel文件中,并使用Java原生的压缩类库将Excel文件压缩为zip文件。
-
将压缩后的文件作为响应内容,发送给客户端。
下面是一个示例代码,演示了如何使用SpringBoot MyBatisPlus分页查询、压缩文件、导出大数据量的功能:
@GetMapping("/exportOrders")
public ResponseEntity<byte[]> exportOrders() throws IOException
// 定义信息
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gzipOut = new GZIPOutputStream(out);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(gzipOut));
String header = "id,address,launchName,orderNum,orderRemark";
writer.write(header);
writer.newLine();
// 设置每页大小
int pageNum = 1;
int pageSize = 2000;
boolean hasNextPage = true;
// 分页查询需要导出的数据
while (hasNextPage)
Page<RbgtOrder> page = new Page<>(pageNum, pageSize);
IPage<RbgtOrder> orderPage = orderMapper.selectPage(page, null);
List<RbgtOrder> orderList = orderPage.getRecords();
System.out.println("当前页数-> pageNum:" + pageNum + ",总页数:pages:" + orderPage.getPages() + "");
if (CollectionUtils.isNotEmpty(orderList))
for (RbgtOrder order : orderList)
String line = order.getId() + "," + order.getAddress() + "," + order.getLaunchName() + "," + order.getOrderNum() + "," + order.getOrderRemark();
writer.write(line);
writer.newLine();
pageNum++;
if (pageNum == orderPage.getPages())
hasNextPage = false;
writer.flush();
writer.close();
gzipOut.finish();
byte[] bytes = out.toByteArray();
// 生成对应压缩文件到制定目录(后续可以结合导出中间件,供客户端下载 -> 适用于大数据量导出,没问题)
InputStream inputStream = new ByteArrayInputStream(bytes);
MultipartFile file = new MockMultipartFile(ContentType.OCTET_STREAM.toString(), inputStream);
file.transferTo(new File(getResourcesPath() + "orders.csv.gz"));
// 请求页面输出压缩文件(适用于小数据量导出,没问题)
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("attachment", "orders.csv.gz");
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
/**
* 查询 - 实时获取resources实时新增的文件目录
*
* @param
* @return java.lang.String
* @Author 俞春旺
* @Date 下午 07:29:28 2023年3月30日 0030
**/
public static String getResourcesPath()
// 这里需要注意的是ApplicationHome是属于SpringBoot的类
// 获取项目下resources/static/img路径
ApplicationHome applicationHome = new ApplicationHome(RbgtOrderController.class);
// 保存目录位置根据项目需求可随意更改 - win和linux目录不一样
String path = applicationHome.getDir().getParentFile().getParentFile().getAbsolutePath() + "/src/main/resources/temp/";
System.out.println("path:" + path);
return path;
在这个示例代码中,我们使用了MyBatis-Plus的lambdaQuery方法进行了分页查询,同时使用了流式查询功能避免了内存占用过大的问题。在每次查询一定数量的数据后,我们将数据写入Excel文件并将Excel文件写入到临时文件中。最后,我们将临时文件压缩为zip文件并将其作为响应内容发送给客户端。
需要注意的是,这里使用了Java原生的压缩类库,并且每次只压缩一定数量的数据,以避免占用过多的内存和文件系统资源。如果需要导出的数据量非常大,建议考虑使用专门的压缩库或者将数据存储到分布式文件系统中。
其实说白了,就是时间换空间,后续可以结合消息中间件或者中间导出服务,将压缩文件上传到自己服务器,在通过客户端下载链接即可
优雅写代码系统springboot+mybatis+pagehelper+mybatisplus+druid教你如何优雅写代码
文章目录
springboot 融合了很多插件。springboot相比spring来说有一下有点
- 自动配置: 针对很多spring的应用程序,springboot提供了很多自动配置
- 起步依赖: 告诉springboot你需要什么,他就会引入需要的库
- 命令行界面:springboot的可选特性
- Autuator: 监控springboot项目的运行情况
spring基本搭建
- springboot的配置很简单。在pom中继承springboot的pom .然后依赖一下pom就可以继承所需的jar了
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
- 出了jar外。我们pom中配置一个插件就行了
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
整合mybatis
【详见feature/0002/spring-mybatis 分支】
- 之前我们梳理过mybatis的运行机制及注意点。javaweb的开发时离不开spring的。一个完整的框架是离不开spirng的。所以spring整合mybatis势在必行。我们下面实现如何在spring下融合mybatis及其优秀的一些插件搭建架构
pom配置
- 我们在springboot项目的pom中继续添加我们mybatis的jar就完成了第一步。
- 一个是mybatis与spring的整合jar 。 开启springboot加载mybatis项目
- 一个是spring的aopjar包。主要是实现mybatis的事务问题
- 一个是mysql的jar包。这里主要看你自己的需求。如果你的项目中使用oracle那么久加入oracle的坐标就行了
- druid是管理数据的数据源
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
mybatis配置
- 上面的pom设置已经准备了mybatis开发阶段的基本jar。 有了上述的包就可以搭建mybatis . 正常我们在我们的springboot项目中配置一个bean配置的类
MybatisConfig
设置数据源
@Bean
@ConfigurationProperties("spring.datasource")
public DataSource primaryDataSource()
// 这里为了演示,暂时写死 。 实际上是可以通过autoConfigure装配参数的
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://192.168.44.130:3306/others?useUnicode=true&characterEncoding=utf8");
return dataSource;
- 在springboot中我们只需要在方法上添加
Bean
注解,就相当于我们在spring的xml中配置的bean标签。在java代码中我们可以进行我们业务处理。个人理解感觉更加的可控。 因为我们使用的mysql。所以这里我们简单的使用druid的数据源
设置sqlsessionfactory
@Bean
public SqlSessionFactory primarySqlSessionFactory()
SqlSessionFactory factory = null;
try
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(primaryDataSource());
sqlSessionFactoryBean.setConfigLocation(new DefaultResourceLoader().getResource("classpath:mybatis-primary.xml"));
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/com/github/zxhtom.**./*.xml"));
factory = sqlSessionFactoryBean.getObject();
catch (Exception e)
e.printStackTrace();
return factory;
- 在mybatis中sqlsesson是我们操作数据库最直接的java类。管理sqlsession就是上面的sqlsessionFactory 。 所以我们配置它也是必须的。因为和spring整合。Mybatis的SqlsessionFactory交由给spring的SqlsessionfactoryBean管理。所以我们先构建SqlSessionFactoryBean。然后配置必须的数据源、Configuration、mybatis的xml路径等等信息
- SqlSessionFactoryBean 设置出了spring的FactoryBean属性意外。最重要的是可以设置Mybatis的sqlsessionfactory的属性。上面我们只是简单的设置了。后期可以根据架构的需求进行不断的完善。
- 可以设置插件、缓存、别名、映射处理器、事务、环境等等所有mybatis的配置
设置扫描
@Bean
public MapperScannerConfigurer mapperScannerConfigurer()
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactory(primarySqlSessionFactory());
//每张表对应的*.java,interface类型的Java文件
mapperScannerConfigurer.setBasePackage("com.github.zxhtom.**.mapper");
return mapperScannerConfigurer;
- springboot 中我们的mapper接口也是交由spring来扫描的。之前mybatis程序我们是一个一个手动加入的。spring的特性可以直接扫描指定路径的所有java类。这里我们就设置了一下mapper的路径。
设置开启事务
- 优秀的架构没有事务是能使用的。我们配置事务也是很简单的。在springboot中我们推荐使用java代码来配置事务的。下面的配置我们为了容易阅读。配置在TransactionConfig类中
@Bean
public DataSourceTransactionManager primaryTransactionManager()
return new DataSourceTransactionManager(dataSource);
- 首先是配置事务管理器。事务管理的是数据源。所以这里我们需要将MybatisConfig中的DataSource加载进来。这里不多说
- 然后配置我们的通知点
@Bean
public TransactionInterceptor txAdvice()
DefaultTransactionAttribute txAttr_REQUIRED = new DefaultTransactionAttribute();
txAttr_REQUIRED.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
DefaultTransactionAttribute txAttr_REQUIRED_READONLY = new DefaultTransactionAttribute();
txAttr_REQUIRED_READONLY.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
txAttr_REQUIRED_READONLY.setReadOnly(true);
NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
source.addTransactionalMethod("add*", txAttr_REQUIRED);
source.addTransactionalMethod("save*", txAttr_REQUIRED);
source.addTransactionalMethod("delete*", txAttr_REQUIRED);
source.addTransactionalMethod("update*", txAttr_REQUIRED);
source.addTransactionalMethod("exec*", txAttr_REQUIRED);
source.addTransactionalMethod("set*", txAttr_REQUIRED);
source.addTransactionalMethod("get*", txAttr_REQUIRED_READONLY);
source.addTransactionalMethod("query*", txAttr_REQUIRED_READONLY);
source.addTransactionalMethod("find*", txAttr_REQUIRED_READONLY);
source.addTransactionalMethod("list*", txAttr_REQUIRED_READONLY);
source.addTransactionalMethod("count*", txAttr_REQUIRED_READONLY);
source.addTransactionalMethod("is*", txAttr_REQUIRED_READONLY);
return new TransactionInterceptor(primaryTransactionManager(), source);
- 最后我们配置一下我们的切面、切点
@Bean
public Advisor txAdviceAdvisor()
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
return new DefaultPointcutAdvisor(pointcut, txAdvice());
@Bean
public Advisor txAdviceAdvisor()
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
return new DefaultPointcutAdvisor(pointcut, txAdvice());
- 最后说明下。TransactionConfig这个类因为配置事务、拦截所以我们需要加入如下注解在雷伤
@Configuration
@Aspect
@EnableTransactionManagement
资源放行
-
以上就是mybatis基本的一个配置了。但是这个时候还是不能使用的。因为我们的mapper对应的xml是放在java目录下的。正常src下都是java。xml文件在maven编译时不放行的。我们需要特殊处理下
-
在pom的build下加入放行配置
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
<include>**/*.yaml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
- 加入如下配置后别忘记了clean一下在运行。防止缓存。clean之后看看target下文件
测试
- 为了方便测试我们需要编写测试类。
@SpringBootTest(classes = Application.class)
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public class TestDataSource
@Autowired
private TestMapper testMapper;
@Test
public void test()
List<Map<String, Object>> test = testMapper.test();
System.out.println(test);
结果
- 这里是我查询数据库中的一条数据。主要是测试springboot整合mybatis的流程。到这里mybatis就整合完了。
思考&&疑问
- 上面的配置我们是接入mybatis了。但是在笔者其他架构上测试过。在MybatisConfig这个类中配置数据源的时候可以直接new出DruidDataSource就可以了。springboot会自动通过DataSourceProperties这个类获取到数据源信息的。但是笔者这里一直没有尝试成功。至于原理更是没有搞懂了。这里留下一个彩蛋希望知道的朋友能说说这事怎么回事
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource primaryDataSource()
return new DruidDataSource();
- 上述代码和本案例中相比就少了很多配置。虽然有的朋友说可以通过映射实体来做。但是直接new对象对我们而言更简单。疑问
使用通用mapper功能
【详见feature/0002/spring-mybatis-tk-page 分支】
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>3.3.9</version>
</dependency>
-
这些插件其实就是改写我们之前学习的myabtis四大组件中的某一个组件而实现的。所以不管是通用mapper还是后面要说的myabtis-plus都是需要重新改写我们的myabtis配置类的。
-
首先我们想接入通用mapper时,我们需要改用tk提供的扫包配置
@Bean
public MapperScannerConfigurer mapperScannerConfigurer()
tk.mybatis.spring.mapper.MapperScannerConfigurer mapperScannerConfigurer = new tk.mybatis.spring.mapper.MapperScannerConfigurer();
//MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactory(primarySqlSessionFactory());
//每张表对应的*.java,interface类型的Java文件
mapperScannerConfigurer.setBasePackage("com.github.zxhtom.**.mapper");
return mapperScannerConfigurer;
- 然后添加通用mapper配置
@Bean()
public MapperHelper primaryMapperHelper() throws Exception
MapperHelper mapperHelper = new MapperHelper();
Config config = mapperHelper.getConfig();
//表名字段名使用驼峰转下划线大写形式
config.setStyle(Style.camelhumpAndUppercase);
mapperHelper.registerMapper(Mapper.class);
mapperHelper.processConfiguration(primarySqlSessionFactory().getConfiguration());
return mapperHelper;
- 测试代码就很简单了。
User user = tkMapper.selectByPrimaryKey(1);
使用mybatis-plus
【详见feature/0002/spring-mybatisplus 分支】
- mybatis-plus 实际上就是通用mapper 。这里可以理解就是不同的实现。接入mybatis-plus其实很简单。首先我们引入坐标
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
- 然后我们创建实体
@Data
@TableName(value = "person_info_large")
public class User
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
private String account;
private String name;
private String area;
private String title;
private String motto;
- 然后就是编写我们的Mapper 。需要继承BaseMapper
public interface PlusMapper extends BaseMapper<User>
- 上述的编写之后基本就可以查询了。但是注意一下我们需要修改上面的myabtisCOnfog这个类。因为接入mybatisplus后需要我们用mybatisplus中的sqlsessionbean。
@Bean
public SqlSessionFactory primarySqlSessionFactory()
SqlSessionFactory factory = null;
try
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(primaryDataSource());
sqlSessionFactoryBean.setConfigLocation(new DefaultResourceLoader().getResource("classpath:mybatis-primary.xml"));
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:com/github/zxhtom.**./*.xml"));
sqlSessionFactoryBean.setTypeAliasesPackage("com.github.zxhtom.**.model");
factory = sqlSessionFactoryBean.getObject();
catch (Exception e)
e.printStackTrace();
return factory;
- 和通用mapper一样。就这样我们就可以调用BaseMapper为我们提供的方法操作数据库了。
@Test
public void plusTest()
User user = plusMapper.selectById(1);
System.out.println(user);
使用分页插件
mybatis-plus自带分页
【详见feature/0002/spring-mybatisplus 分支】
- 首先我们使用mybatis plus自带的分页插件,将插件类注入到容器中
@Bean
public PaginationInterceptor paginationInterceptor()
return new PaginationInterceptor();
- 然后我们查询的构造一个Page对象就行了。参数分别是第几页、展示条数
return plusMapper.selectPage(page, null);
- 就是这么简单。使用简单原理才是我们学习的重点。喜欢myabtis插件原理的可以留言。抽空可以研究
github分页插件
【详见feature/0002/spring-mybatis-tk-page 分支】
-
因为myatis-plus自带了分页插件。上面也展示了如何使用myabtis-plus插件。还有一个github上开元的分页插件。这里就和通用mapper进行组合使用
-
pagehelper官方更新还是挺勤奋的。提供了pagehelper 和springboot auto装配两种。 笔者这里测试了
pagehelper-spring-boot-starter
这个包不适合本案例中。因为本案例中扫描mapper路径是通过MapperScannerConfigurer
注册的。如果使用pagehelper-spring-boot-starter
的话就会导致分页拦截器失效。我们看看源码
- 这是因为这版本提供了springboot自动装配。但是自动装配的代码中进行添加拦截器的时候sqlsessionfactory这个时候还没有进行扫描mapper.也就没有进行addMapper 。 所以这个时候添加的拦截器拦截不到我们的mapper . 如果非要使用这个版本的话。我们扫描mapper就不能通过
MapperScannerConfigurer
. 经过笔者测试。需要在MybatisConfig类上添加扫描注解@MapperScan(basePackages = "com.github.zxhtom.**.mapper", sqlSessionFactoryRef = "primarySqlSessionFactory")
使用常规版本
- 为了符合本版本宗旨 。 我们这里使用如下坐标
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.7</version>
</dependency>
- 使用这个版本pagehelper。 没有了springboot的自动装配了。我们可以自己添加插件。添加插件有两种方式。想自动装配版本一样通过Configuration进行添加不过我们通过Condition条件选择添加的时机 。
- 还有一个简单的方式就是通过
SqlSessionFactoryBean
设置sqlSessionFactoryBean.setPlugins(new Interceptor[]new PageInterceptor());
。 - 因为通过sqlsessionFactoryBean添加的插件和在settings文件中添加是一样的。在通过XmlFactory.build Configuration对象是会自动将插件装在上。这个时候mapper也都扫描过了。
@Bean
public SqlSessionFactory primarySqlSessionFactory()
SqlSessionFactory factory = null;
try
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(primaryDataSource());
sqlSessionFactoryBean.setConfigLocation(new DefaultResourceLoader().getResource(以上是关于SpringBoot+MyBatisPlus:分页查询压缩文件导出大数据量解决方案的主要内容,如果未能解决你的问题,请参考以下文章
springboot+mybatisplus+sqlserver(2008SR)整合分页问题
Vue+SpringBoot 前后端分离实战(mybatisplus多表分页查询+博客显示)