Spring Batch读取txt文件并写入数据库的方法教程

项目需求

近日需要实现用户推荐相关的功能,也就是说向用户推荐他可能喜欢的东西。

我们的数据分析工程师会将用户以及用户可能喜欢的东西整理成文档给我,我只需要将数据从文档中读取出来,然后对数据进行进一步的清洗(例如去掉特殊符号,长度如果太长则截取)。然后将处理后的数据存入数据库(Mysql)。

所以分为三步:

  • 读取文档获得数据
  • 对获得的数据进行处理
  • 更新数据库(新增或更新)

考虑到这个数据量以后会越来越大,这里没有使用 poi 来读取数据,而直接使用了 SpringBatch。

实现步骤

本文假设读者已经能够使用 SpringBoot 连接处理 Mysql,所以这部分文中会省略。

1、创建 Maven 项目,并在 pom.xml 中添加依赖

<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>1.5.2.RELEASE</version>
</parent>
<properties>
 <java.version>1.8</java.version>
</properties>
<dependencies>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-batch</artifactId>
 </dependency>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
 </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>1.2.0</version>
 </dependency>
 <!-- 工具类依赖-->
 <dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.12.6</version>
 </dependency>
 <dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.4</version>
 </dependency>
 <!-- 数据库相关依赖 -->
 <dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
 </dependency>
 <dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.0.26</version>
 </dependency>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
</dependencies>

这里是这个小项目中用到的所有依赖,包括连接数据库的依赖以及工具类等。

2、编写 Model 类

我们要从文档中读取的有效列就是 uid,tag,type,就是用户 ID,用户可能包含的标签(用于推送),用户类别(用户用户之间互相推荐)。

UserMap.java 中的 @Entity,@Column 注解,是为了利用 JPA 生成数据表而写的,可要可不要。

UserMap.java

@Data
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
//@Entity(name = "user_map")
public class UserMap extends BaseModel {
 @Column(name = "uid", unique = true, nullable = false)
 private Long uid;
 @Column(name = "tag")
 private String tag;
 @Column(name = "type")
 private Integer type;
}

3、实现批处理配置类

BatchConfiguration.java

@Configuration
@EnableBatchProcessing
public class BatchConfiguration {
 @Autowired
 public JobBuilderFactory jobBuilderFactory;
 @Autowired
 public StepBuilderFactory stepBuilderFactory;
 @Autowired
 @Qualifier("prodDataSource")
 DataSource prodDataSource;
 @Bean
 public FlatFileItemReader<UserMap> reader() {
  FlatFileItemReader<UserMap> reader = new FlatFileItemReader<>();
  reader.setResource(new ClassPathResource("c152.txt"));
  reader.setLineMapper(new DefaultLineMapper<UserMap>() {{
   setLineTokenizer(new DelimitedLineTokenizer("|") {{
    setNames(new String[]{"uid", "tag", "type"});
   }});
   setFieldSetMapper(new BeanWrapperFieldSetMapper<UserMap>() {{
    setTargetType(UserMap.class);
   }});
  }});
  return reader;
 }
 @Bean
 public JdbcBatchItemWriter<UserMap> importWriter() {
  JdbcBatchItemWriter<UserMap> writer = new JdbcBatchItemWriter<>();
  writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>());
  writer.setSql("INSERT INTO user_map (uid,tag,type) VALUES (:uid, :tag,:type)");
  writer.setDataSource(prodDataSource);
  return writer;
 }
 @Bean
 public JdbcBatchItemWriter<UserMap> updateWriter() {
  JdbcBatchItemWriter<UserMap> writer = new JdbcBatchItemWriter<>();
  writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>());
  writer.setSql("UPDATE user_map SET type = (:type),tag = (:tag) WHERE uid = (:uid)");
  writer.setDataSource(prodDataSource);
  return writer;
 }
 @Bean
 public UserMapItemProcessor processor(UserMapItemProcessor.ProcessStatus processStatus) {
  return new UserMapItemProcessor(processStatus);
 }
 @Bean
 public Job importUserJob(JobCompletionNotificationListener listener) {
  return jobBuilderFactory.get("importUserJob")
    .incrementer(new RunIdIncrementer())
    .listener(listener)
    .flow(importStep())
    .end()
    .build();
 }
 @Bean
 public Step importStep() {
  return stepBuilderFactory.get("importStep")
    .<UserMap, UserMap>chunk(100)
    .reader(reader())
    .processor(processor(IMPORT))
    .writer(importWriter())
    .build();
 }
 @Bean
 public Job updateUserJob(JobCompletionNotificationListener listener) {
  return jobBuilderFactory.get("updateUserJob")
    .incrementer(new RunIdIncrementer())
    .listener(listener)
    .flow(updateStep())
    .end()
    .build();
 }
 @Bean
 public Step updateStep() {
  return stepBuilderFactory.get("updateStep")
    .<UserMap, UserMap>chunk(100)
    .reader(reader())
    .processor(processor(UPDATE))
    .writer(updateWriter())
    .build();
 }
}

prodDataSource 是假设用户已经设置好的,如果不知道怎么配置,也可以参考之前的文章进行配置:Springboot 集成 Mybatis。

reader(),这方法从文件中读取数据,并且设置了一些必要的参数。紧接着是写操作 importWriter()updateWriter() ,读者看其中一个就好,因为我这里是需要更新或者修改的,所以分为两个。

processor(ProcessStatus status) ,该方法是对我们处理数据的类进行实例化,这里我根据 status 是 IMPORT 还是 UPDATE 来获取不同的处理结果。

其他的看代码就可以看懂了,哈哈,不详细说了。

4、将获得的数据进行清洗

UserMapItemProcessor.java

public class UserMapItemProcessor implements ItemProcessor<UserMap, UserMap> {
 private static final int MAX_TAG_LENGTH = 200;

 private ProcessStatus processStatus;
 public UserMapItemProcessor(ProcessStatus processStatus) {
  this.processStatus = processStatus;
 }
 @Autowired
 IUserMapService userMapService;
 private static final String TAG_PATTERN_STR = "^[a-zA-Z0-9\\u4E00-\\u9FA5_-]+$";
 public static final Pattern TAG_PATTERN = Pattern.compile(TAG_PATTERN_STR);
 private static final Logger LOG = LoggerFactory.getLogger(UserMapItemProcessor.class);
 @Override
 public UserMap process(UserMap userMap) throws Exception {
  Long uid = userMap.getUid();
  String tag = cleanTag(userMap.getTag());
  Integer label = userMap.getType() == null ? Integer.valueOf(0) : userMap.getType();
  if (StringUtils.isNotBlank(tag)) {
   Map<String, Object> params = new HashMap<>();
   params.put("uid", uid);
   UserMap userMapFromDB = userMapService.selectOne(params);
   if (userMapFromDB == null) {
    if (this.processStatus == ProcessStatus.IMPORT) {
     return new UserMap(uid, tag, label);
    }
   } else {
    if (this.processStatus == ProcessStatus.UPDATE) {
     if (!tag.equals(userMapFromDB.getTag()) && !label.equals(userMapFromDB.getType())) {
      userMapFromDB.setType(label);
      userMapFromDB.setTag(tag);
      return userMapFromDB;
     }
    }
   }
  }
  return null;
 }
 /**
  * 清洗标签
  *
  * @param tag
  * @return
  */
 private static String cleanTag(String tag) {
  if (StringUtils.isNotBlank(tag)) {
   try {
    tag = tag.substring(tag.indexOf("{") + 1, tag.lastIndexOf("}"));
    String[] tagArray = tag.split(",");
    Optional<String> reduce = Arrays.stream(tagArray).parallel()
      .map(str -> str.split(":")[0])
      .map(str -> str.replaceAll("\'", ""))
      .map(str -> str.replaceAll(" ", ""))
      .filter(str -> TAG_PATTERN.matcher(str).matches())
      .reduce((x, y) -> x + "," + y);
    Function<String, String> str = (s -> s.length() > MAX_TAG_LENGTH ? s.substring(0, MAX_TAG_LENGTH) : s);
    return str.apply(reduce.get());
   } catch (Exception e) {
    LOG.error(e.getMessage(), e);
   }
  }
  return null;
 }
 protected enum ProcessStatus {
  IMPORT,
  UPDATE;
 }
 public static void main(String[] args) {
  String distinctTag = cleanTag("Counter({'《重新定义》': 3, '轻想上的轻小说': 3, '小说': 2, 'Fate': 2, '同人小说': 2, '雪狼八组': 1, " +
    "'社会': 1, '人文': 1, '短篇': 1, '重新定义': 1, 'AMV': 1, '《FBD》': 1, '《雪狼六组》': 1, '战争': 1, '《灰羽联盟》': 1, " +
    "'谁说轻想没人写小说': 1})");
  System.out.println(distinctTag);
 }
}

读取到的数据格式如 main() 方法所示,清理之后的结果如:

轻想上的轻小说,小说,Fate,同人小说,雪狼八组,社会,人文,短篇,重新定义,AMV,战争,谁说轻想没人写小说 。

去掉了特殊符号以及数字等。使用了 Java8 的 Lambda 表达式。

并且这里在处理的时候,判断如果该数据用户已经存在,则进行更新,如果不存在,则新增。

5、Job 执行结束回调类

JobCompletionNotificationListener.java

@Component
public class JobCompletionNotificationListener extends JobExecutionListenerSupport {
 private static final Logger log = LoggerFactory.getLogger(JobCompletionNotificationListener.class);
 private final JdbcTemplate jdbcTemplate;
 @Autowired
 public JobCompletionNotificationListener(JdbcTemplate jdbcTemplate) {
  this.jdbcTemplate = jdbcTemplate;
 }
 @Override
 public void afterJob(JobExecution jobExecution) {
  System.out.println("end .....");
 }
}

具体的逻辑可自行实现。

完成以上几个步骤,运行项目,就可以读取并写入数据到数据库了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • spring batch 读取多个文件数据导入数据库示例

    项目的目录结构 需要读取文件的的数据格式 applicatonContext.xml的配置 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p=

  • Spring Batch入门教程篇

    SpringBatch介绍: SpringBatch 是一个大数据量的并行处理框架.通常用于数据的离线迁移,和数据处理,⽀持事务.并发.流程.监控.纵向和横向扩展,提供统⼀的接⼝管理和任务管理;SpringBatch是SpringSource和埃森哲为了统一业界并行处理标准为广大开发者提供方便开发的一套框架. 官方地址:github.com/spring-projects/spring-batch SpringBatch 本身提供了重试,异常处理,跳过,重启.任务处理统计,资源管理等特性,这些特

  • Spring batch批处理框架

    spring batch框架的简介 批处理任务是大多数IT项目的一个重要组成部分,批处理在业务系统中负责处理海量的数据,无须人工干预就能够自动高效的进行复杂的数据分析和处理.批处理会定期读入批量数据,经过相应的业务处理进行归档的业务操作,批处理的特征是自动执行,处理的数据量大,定时执行.将整个批处理的流程按逻辑划分可以分为读数据,处理数据和写数据. spring batch对批处理本身的特性进行了抽象,将批处理作业抽象为job和job step,将批处理的处理过程分解为数据读,数据处理和数据写.

  • Spring Batch读取txt文件并写入数据库的方法教程

    项目需求 近日需要实现用户推荐相关的功能,也就是说向用户推荐他可能喜欢的东西. 我们的数据分析工程师会将用户以及用户可能喜欢的东西整理成文档给我,我只需要将数据从文档中读取出来,然后对数据进行进一步的清洗(例如去掉特殊符号,长度如果太长则截取).然后将处理后的数据存入数据库(Mysql). 所以分为三步: 读取文档获得数据 对获得的数据进行处理 更新数据库(新增或更新) 考虑到这个数据量以后会越来越大,这里没有使用 poi 来读取数据,而直接使用了 SpringBatch. 实现步骤 本文假设读

  • Java读取txt文件和写入txt文件的简单实例

    写Java程序时经常碰到要读如txt或写入txt文件的情况,但是由于要定义好多变量,经常记不住,每次都要查,特此整理一下,简单易用,方便好懂! package edu.thu.keyword.test; import java.io.File; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream;

  • C#逐行分元素读取记事本数据并写入数据库的方法

    本文实例讲述了C#逐行分元素读取记事本数据并写入数据库的方法.分享给大家供大家参考.具体分析如下: 其实这里最关键的一个方法是 StreamReader类里的 ReadLine();这个方法可以逐行读取txt流里面的数据.写了个简单的demo,已经加上了详细的注释说明. ok,好了,不废话,下面直接上代码 复制代码 代码如下: public void InputData()  {      DataTable dt = new DataTable();      string strFilePa

  • Python实现读取txt文件并转换为excel的方法示例

    本文实例讲述了Python实现读取txt文件并转换为excel的方法.分享给大家供大家参考,具体如下: 这里的txt文件内容格式为: 892天平天国定都在?A开封B南京C北京(B) Python代码如下: # coding=utf-8 ''''' main function:主要实现把txt中的每行数据写入到excel中 ''' ################# #第一次执行的代码 import xlwt #写入文件 import xlrd #打开excel文件 import os txtFi

  • Python实现读取TXT文件数据并存进内置数据库SQLite3的方法

    本文实例讲述了Python实现读取TXT文件数据并存进内置数据库SQLite3的方法.分享给大家供大家参考,具体如下: 当TXT文件太大,计算机内存不够时,我们可以选择按行读取TXT文件,并将其存储进Python内置轻量级splite数据库,这样可以加快数据的读取速度,当我们需要重复读取数据时,这样的速度加快所带来的时间节省是非常可观的,比如,当我们在训练数据时,要迭代10万次,即要从文件中读取10万次,即使每次只加快0.1秒,那么也能节省几个小时的时间了. #创建数据库并把txt文件的数据存进

  • spring mvc 读取xml文件数据库配置参数的方法

    本文主要介绍怎么通过属性注入与构造器注入实现把我们项目中要用到的数据库参数放到xml文件里面去,方便部署. spring mvc 4.2.6项目 SQL Server 2008数据库 本文介绍的主要使用ApplicationContext以及其实现类实现.主要用到的是ClassPathXmlApplicationContext. ClassPathXmlApplicationContext:从类路径ClassPath中寻找指定的XML配置文件,找到并装载 完成ApplicationContext

  • php逐行读取txt文件写入数组的方法 原创

    本文实例讲述了php逐行读取txt文件写入数组的方法.分享给大家供大家参考.具体如下: 假设有user.txt文件如下: user01 user02 user03 user04 user05 user06 user07 user08 user09 user10 user11 user12 逐行读取user.txt并写入数组的方法如下: $file = fopen("username.txt", "r"); $user=array(); $i=0; //输出文本中所有

  • 使用python读取txt文件的内容,并删除重复的行数方法

    注意,本文代码是使用在txt文档上,同时txt文档中的内容每一行代表的是图片的名字. #coding:utf-8 import shutil readDir = "原文件绝对路经" writeDir = "写入文件的绝对路径" #txtDir = "/home/fuxueping/Desktop/1" lines_seen = set() outfile=open(writeDir,"w") f = open(readDir,

  • python批量读取txt文件为DataFrame的方法

    我们有时候会批量处理同一个文件夹下的文件,并且希望读取到一个文件里面便于我们计算操作.比方我有下图一系列的txt文件,我该如何把它们写入一个txt文件中并且读取为DataFrame格式呢? 首先我们要用到glob模块,这个python内置的模块可以说是非常的好用. glob.glob('*.txt') 得到如下结果: all.txt是我最后得到的结果文件.可以见返回的是一个包含txt文件名称的列表,当然如果你的文件夹下面只有txt文件,那么你用os.listdir()可以得到一个一样的列表 然后

  • java读取txt文件并输出结果

    这篇文章主要介绍了java读取txt文件并输出结果,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 描述: 1.java读取指定txt文件并解析 文件格式: 代码: package com.thinkgem.wlw.modules.midea; import java.io.*; import java.util.ArrayList; import java.util.List; /** * @Author: zhouhe * @Date: 20

随机推荐