# Handling Errors When Importing CSV Data into a Database in Spring Boot

<div><strong>Source</strong>: <a href='https://www.tuanh.net/blog/java/handling-errors-when-importing-csv-data-into-a-database-in-spring-boot'>Handling Errors When Importing CSV Data into a Database in Spring Boot</a></div><div class="line-post"> 
 <h2 class="title-post">1. Why CSV Data Import Is Tricky</h2> 
</div>
<div class="line-post">
  Importing data from CSV files is deceptively simple. At first glance, it might seem like just reading lines and inserting them into a database. But real-world scenarios involve: 
</div>
<div class="line-post"> 
 <ul> 
  <li><strong>Invalid Data</strong>: Missing fields, incorrect formats, or unsupported characters.</li> 
  <li><strong>Duplicate Entries</strong>: Repeated records that violate unique constraints.</li> 
  <li><strong>Large File Sizes</strong>: Overloading memory when dealing with millions of records.</li> 
  <li><strong>Concurrency Issues</strong>: Multiple users uploading files simultaneously.</li> 
  <li><strong>Partial Failures</strong>: Some records succeed while others fail, leaving the database in an inconsistent state.</li> 
 </ul> 
</div>
<div class="line-post">
  Each of these challenges demands careful planning and robust error-handling mechanisms. 
</div>
<div class="line-post"> 
 <h3 class="sub1">1.1 Reading the CSV File</h3> 
</div>
<div class="line-post">
  Spring Boot provides various libraries to read CSV files, such as OpenCSV or Apache Commons CSV. Let’s start by setting up a basic example to read a CSV file. 
</div>
<pre class="code">@Component<br><span class="keyword-css">public</span> <span class="keyword-css">class</span> CSVReader {<br><br>    <span class="keyword-css">public</span> List&lt;String[]&gt; readCSV(String filePath) <span class="keyword-css">throws</span> IOException {<br>        List&lt;String[]&gt; data = <span class="keyword-css">new</span> ArrayList&lt;&gt;();<br>        <span class="keyword-css">try</span> (BufferedReader br = <span class="keyword-css">new</span> BufferedReader(<span class="keyword-css">new</span> FileReader(filePath))) {<br>            String line;<br>            <span class="keyword-css">while</span> ((line = br.readLine()) != null) {<br>                String[] values = line.split(",");<br>                data.add(values);<br>            }<br>        } <span class="keyword-css">catch</span> (IOException e) {<br>            <span class="keyword-css">throw</span> <span class="keyword-css">new</span> IOException("Error reading CSV file: " + e.getMessage(), e);<br>        }<br>        <span class="keyword-css">return</span> data;<br>    }<br>}</pre>
<div class="line-post">
  In this code: 
</div>
<div class="line-post"> 
 <ul> 
  <li>BufferedReader is used to read the file line by line.</li> 
  <li>Lines are split using commas, but this is a simplistic approach and may fail for quoted fields or escaped commas.</li> 
 </ul> 
</div>
<div class="line-post"> <strong>Improvement</strong>: Using a library like OpenCSV avoids edge cases and provides cleaner handling. 
</div>
<div class="line-post"> 
 <h3 class="sub1">1.2 Validating Data</h3> 
</div>
<div class="line-post">
  Data validation is crucial to avoid importing corrupted or incomplete data. Use a combination of Java Bean Validation and custom validation logic. 
</div>
<pre class="code"><span class="keyword-css">public</span> <span class="keyword-css">class</span> CSVRecord {<br>    @NotBlank(message = "Name cannot be blank")<br>    <span class="keyword-css">private</span> String name;<br><br>    @Email(message = "Invalid email format")<br>    <span class="keyword-css">private</span> String email;<br><br>    @Pattern(regexp = "\d{10}", message = "Phone number must be 10 digits")<br>    <span class="keyword-css">private</span> String phone;<br><br>    // Getters and Setters<br>}</pre>
<div class="line-post">
  Add validation during processing: 
</div>
<pre class="code">@Service<br><span class="keyword-css">public</span> <span class="keyword-css">class</span> CSVProcessor {<br><br>    <span class="keyword-css">public</span> <span class="keyword-css">void</span> validateAndSave(List&lt;String[]&gt; records) {<br>        <span class="keyword-css">for</span> (String[] record : records) {<br>            <span class="keyword-css">try</span> {<br>                CSVRecord csvRecord = mapToRecord(record);<br>                validate(csvRecord);<br>                saveToDatabase(csvRecord);<br>            } <span class="keyword-css">catch</span> (ConstraintViolationException e) {<br>                logError(record, e);<br>            }<br>        }<br>    }<br><br>    <span class="keyword-css">private</span> CSVRecord mapToRecord(String[] record) {<br>        CSVRecord csvRecord = <span class="keyword-css">new</span> CSVRecord();<br>        csvRecord.setName(record[0]);<br>        csvRecord.setEmail(record[1]);<br>        csvRecord.setPhone(record[2]);<br>        <span class="keyword-css">return</span> csvRecord;<br>    }<br><br>    <span class="keyword-css">private</span> <span class="keyword-css">void</span> validate(CSVRecord csvRecord) {<br>        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();<br>        Set&lt;ConstraintViolation&lt;CSVRecord&gt;&gt; violations = validator.validate(csvRecord);<br>        <span class="keyword-css">if</span> (!violations.isEmpty()) {<br>            <span class="keyword-css">throw</span> <span class="keyword-css">new</span> ConstraintViolationException(violations);<br>        }<br>    }<br><br>    <span class="keyword-css">private</span> <span class="keyword-css">void</span> saveToDatabase(CSVRecord csvRecord) {<br>        // Save to the database logic<br>    }<br><br>    <span class="keyword-css">private</span> <span class="keyword-css">void</span> logError(String[] record, Exception e) {<br>        System.err.println("Error processing record: " + Arrays.toString(record) + " - " + e.getMessage());<br>    }<br>}</pre>
<div class="line-post"> 
 <h3 class="sub1">1.3 Handling Duplicate Entries</h3> 
</div>
<div class="line-post">
  When duplicate entries are present, database constraints can throw exceptions. Use ON DUPLICATE KEY SQL statements or implement conflict resolution in the code. 
</div>
<pre class="code">@Transactional<br><span class="keyword-css">public</span> <span class="keyword-css">void</span> saveToDatabase(CSVRecord csvRecord) {<br>    Optional&lt;User&gt; existingUser = userRepository.findByEmail(csvRecord.getEmail());<br>    <span class="keyword-css">if</span> (existingUser.isPresent()) {<br>        System.out.println("Duplicate record found: " + csvRecord.getEmail());<br>        <span class="keyword-css">return</span>; // Skip or update existing record<br>    }<br>    userRepository.save(csvRecord.toEntity());<br>}</pre>
<div class="line-post"> 
 <h3 class="sub1">1.4 Managing Large Files</h3> 
</div>
<div class="line-post">
  To handle large files, process the CSV in chunks instead of loading it entirely into memory. 
</div>
<pre class="code"><span class="keyword-css">public</span> <span class="keyword-css">void</span> processLargeCSV(String filePath) <span class="keyword-css">throws</span> IOException {<br>    <span class="keyword-css">try</span> (Stream&lt;String&gt; lines = Files.lines(Paths.get(filePath))) {<br>        lines.skip(1) // Skip header<br>            .map(<span class="keyword-css">this</span>::parseLine)<br>            .forEach(<span class="keyword-css">this</span>::processRecord);<br>    }<br>}<br><br><span class="keyword-css">private</span> <span class="keyword-css">void</span> processRecord(String[] record) {<br>    <span class="keyword-css">try</span> {<br>        // Process each record<br>    } <span class="keyword-css">catch</span> (Exception e) {<br>        // Handle errors<br>    }<br>}</pre>
<div class="line-post"> 
 <h2 class="title-post">2. Advanced Error-Handling Techniques</h2> 
</div>
<div class="line-post"> 
 <h3 class="sub1">2.1 Logging and Monitoring</h3> 
</div>
<div class="line-post">
  Set up centralized logging using tools like ELK Stack or Graylog. Log errors with sufficient context for debugging. 
</div>
<pre class="code"><span class="keyword-css">private</span> <span class="keyword-css">void</span> logError(String[] record, Exception e) {<br>    logger.error("Error processing record: {} - {}", Arrays.toString(record), e.getMessage());<br>}</pre>
<div class="line-post"> 
 <h3 class="sub1">2.2 Retry Mechanism</h3> 
</div>
<div class="line-post">
  Use a retry mechanism for transient errors, such as database deadlocks or temporary network failures. 
</div>
<pre class="code">@Retryable(<br>    value = {SQLException.<span class="keyword-css">class</span>},<br>    maxAttempts = 3,<br>    backoff = @Backoff(delay = 2000)<br>)<br><span class="keyword-css">public</span> <span class="keyword-css">void</span> saveToDatabaseWithRetry(CSVRecord csvRecord) {<br>    userRepository.save(csvRecord.toEntity());<br>}</pre>
<div class="line-post"> 
 <h3 class="sub1">2.3 Transaction Management</h3> 
</div>
<div class="line-post">
  Use Spring’s @Transactional annotation to ensure atomicity. 
</div>
<pre class="code">@Transactional<br><span class="keyword-css">public</span> <span class="keyword-css">void</span> importCSV(String filePath) {<br>    List&lt;String[]&gt; records = csvReader.readCSV(filePath);<br>    <span class="keyword-css">for</span> (String[] record : records) {<br>        <span class="keyword-css">try</span> {<br>            saveToDatabase(mapToRecord(record));<br>        } <span class="keyword-css">catch</span> (Exception e) {<br>            <span class="keyword-css">throw</span> <span class="keyword-css">new</span> RuntimeException("Failed to process record: " + Arrays.toString(record), e);<br>        }<br>    }<br>}</pre>
<div class="line-post"> 
 <h2 class="title-post">3. Summary and Best Practices</h2> 
</div>
<div class="line-post"> <strong>Validation First</strong>: Validate data before processing. 
</div>
<div class="line-post"> <strong>Error Isolation</strong>: Log errors for problematic records without failing the entire process. 
</div>
<div class="line-post"> <strong>Chunk Processing</strong>: Use batch imports for large files. 
</div>
<div class="line-post"> <strong>Centralized Monitoring</strong>: Log errors and monitor them for patterns. 
</div>
<div class="line-post"> <strong>Retry for Transient Errors</strong>: Implement retries for recoverable errors. 
</div>
<div class="line-post">
  Do you have questions or want to share your challenges? Feel free to comment below! 
</div><p><strong>Read more at </strong>: <a href='https://www.tuanh.net/blog/java/handling-errors-when-importing-csv-data-into-a-database-in-spring-boot'>Handling Errors When Importing CSV Data into a Database in Spring Boot</a></p>
