Showing posts with label SpringBoot. Show all posts
Showing posts with label SpringBoot. Show all posts

Wednesday, March 1, 2023

Call a Stored Procedure from a Oracle Package in SpringBoot Application

 

To create a Spring Boot application that calls an Oracle stored procedure `STORED_PRC_NAME` in a package `PACKAGE_NAME`, follow these steps:

 

1. Set Up Your Spring Boot Project: Use Spring Initializr or your IDE to create a new Spring Boot project with the necessary dependencies.

 

 Step 1: Create the Project

 

1. Initialize a Spring Boot project: Include the following dependencies:

   - Spring Web

   - Spring Data JPA

   - Oracle JDBC

   - Spring Boot Starter JDBC

 

2. Project Structure: Ensure your project has the following structure:

 

   src/main/java/com/example/oracleprocedure

   ── OracleProcedureApplication.java

   ── config

      └── DataSourceConfig.java

   ── repository

      └── OracleRepository.java

   └── service

       └── OracleService.java

 

 

 Step 2: Configure Oracle DataSource

 

Create a configuration class `DataSourceConfig.java` to set up the Oracle DataSource.

 

JAVA CODE:

package com.example.oracleprocedure.config;

 

import oracle.jdbc.pool.OracleDataSource;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.jdbc.datasource.DataSourceTransactionManager;

 

import javax.sql.DataSource;

import java.sql.SQLException;

 

@Configuration

public class DataSourceConfig {

 

    @Bean

    public DataSource dataSource() throws SQLException {

        OracleDataSource dataSource = new OracleDataSource();

        dataSource.setURL("jdbc:oracle:thin:@//your_db_url:1521/your_service_name");

        dataSource.setUser("your_username");

        dataSource.setPassword("your_password");

        return dataSource;

    }

 

    @Bean

    public JdbcTemplate jdbcTemplate(DataSource dataSource) {

        return new JdbcTemplate(dataSource);

    }

 

    @Bean

    public DataSourceTransactionManager transactionManager(DataSource dataSource) {

        return new DataSourceTransactionManager(dataSource);

    }

}

```

 

 Step 3: Create a Repository to Call the Stored Procedure

 

Create a repository class `OracleRepository.java` to handle the stored procedure call.

 

JAVA CODE:

package com.example.oracleprocedure.repository;

 

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.stereotype.Repository;

 

import javax.annotation.PostConstruct;

import javax.sql.DataSource;

import java.sql.Types;

import org.springframework.jdbc.core.SqlOutParameter;

import org.springframework.jdbc.core.SqlParameter;

import org.springframework.jdbc.core.simple.SimpleJdbcCall;

 

@Repository

public class OracleRepository {

 

    private final JdbcTemplate jdbcTemplate;

    private SimpleJdbcCall simpleJdbcCall;

 

    public OracleRepository(JdbcTemplate jdbcTemplate) {

        this.jdbcTemplate = jdbcTemplate;

    }

 

    @PostConstruct

    public void init() {

        simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate)

            .withCatalogName("PACKAGE_NAME") // Package name

            .withProcedureName("STORED_PRC_NAME")    // Procedure name

            .declareParameters(

                new SqlParameter("input_param1", Types.VARCHAR),

                new SqlParameter("input_param2", Types.INTEGER),

                new SqlOutParameter("output_param", Types.VARCHAR)

            );

    }

 

    public String callStoredProcedure(String param1, int param2) {

        Map<String, Object> inParams = new HashMap<>();

        inParams.put("input_param1", param1);

        inParams.put("input_param2", param2);

 

        Map<String, Object> outParams = simpleJdbcCall.execute(inParams);

        return (String) outParams.get("output_param");

    }

}

```

 

 Step 4: Create a Service to Use the Repository

 

Create a service class `OracleService.java` to use the repository.

 

JAVA CODE:

package com.example.oracleprocedure.service;

 

import com.example.oracleprocedure.repository.OracleRepository;

import org.springframework.stereotype.Service;

 

@Service

public class OracleService {

 

    private final OracleRepository oracleRepository;

 

    public OracleService(OracleRepository oracleRepository) {

        this.oracleRepository = oracleRepository;

    }

 

    public String callStoredProcedure(String param1, int param2) {

        return oracleRepository.callStoredProcedure(param1, param2);

    }

}

 

 

 Step 5: Main Application Class

 

Create the main application class `OracleProcedureApplication.java`.

 

JAVA CODE:

package com.example.oracleprocedure;

 

import com.example.oracleprocedure.service.OracleService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.CommandLineRunner;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

 

@SpringBootApplication

public class OracleProcedureApplication implements CommandLineRunner {

 

    @Autowired

    private OracleService oracleService;

 

    public static void main(String[] args) {

        SpringApplication.run(OracleProcedureApplication.class, args);

    }

 

    @Override

    public void run(String... args) throws Exception {

        String result = oracleService.callStoredProcedure("testParam1", 123);

        System.out.println("Stored Procedure Output: " + result);

    }

}

 

 

 Step 6: Add Dependencies in `pom.xml`

 

Here is the complete `pom.xml` for the project:

 

XML Code

<project xmlns="http://maven.apache.org/POM/4.0.0"

         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

 

    <groupId>com.example</groupId>

    <artifactId>oracleprocedure</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <packaging>jar</packaging>

 

    <name>oracle-procedure</name>

    <description>Spring Boot project to call an Oracle stored procedure</description>

 

    <parent>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>3.0.0</version>

        <relativePath/> <!-- lookup parent from repository -->

    </parent>

 

    <properties>

        <java.version>17</java.version>

    </properties>

 

    <dependencies>

        <!-- Spring Boot Starter Web -->

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-web</artifactId>

        </dependency>

 

        <!-- Spring Boot Starter Data JPA -->

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-data-jpa</artifactId>

        </dependency>

 

        <!-- Spring Boot Starter JDBC -->

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-jdbc</artifactId>

        </dependency>

 

        <!-- Oracle JDBC -->

        <dependency>

            <groupId>com.oracle.database.jdbc</groupId>

            <artifactId>ojdbc8</artifactId>

            <version>19.8.0.0</version>

        </dependency>

 

        <!-- Spring Boot Starter Test -->

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-test</artifactId>

            <scope>test</scope>

        </dependency>

    </dependencies>

 

    <build>

        <plugins>

            <plugin>

                <groupId>org.springframework.boot</groupId>

                <artifactId>spring-boot-maven-plugin</artifactId>

            </plugin>

        </plugins>

    </build>

 

</project>

```

 

 Step 7: Run Your Application

 

Run your Spring Boot application. It should call the Oracle stored procedure `STORED_PRC_NAME` and print the result.

 

Make sure your Oracle database is properly set up and accessible from your application. Also, adjust the stored procedure call parameters according to your actual stored procedure's input and output parameters.

Saturday, July 16, 2022

SpringBoot API to return compressed CSV file

Here's an example of a Spring Boot Controller that accepts a request payload of type EmployeeRequest, generates a CSV file based on the input data, compresses the file into a ZIP, and returns the zipped file as a response:

 



Spring Boot Controller

java

import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVPrinter; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import java.io.*; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @RestController @RequestMapping("/api/employee") public class EmployeeController { @PostMapping("/generate") public ResponseEntity<StreamingResponseBody> generateZippedCsv(@RequestBody EmployeeRequest request) { StreamingResponseBody responseBody = outputStream -> { // Create CSV in memory ByteArrayOutputStream csvOutputStream = new ByteArrayOutputStream(); try (CSVPrinter csvPrinter = new CSVPrinter(new PrintWriter(csvOutputStream), CSVFormat.DEFAULT)) { // Add CSV Headers csvPrinter.printRecord("First Name", "Last Name", "Middle Name", "Date of Birth"); // Populate CSV rows with request data for (Employee employee : request.getEmployees()) { csvPrinter.printRecord( employee.getFirstName(), employee.getLastName(), employee.getMiddleName(), employee.getDateOfBirth() ); } } // Create ZIP containing the CSV try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) { zipOutputStream.putNextEntry(new ZipEntry("employees.csv")); zipOutputStream.write(csvOutputStream.toByteArray()); zipOutputStream.closeEntry(); } }; // Set headers for ZIP file download HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); headers.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"employees.zip\""); return ResponseEntity.ok() .headers(headers) .body(responseBody); } }

Supporting Classes

EmployeeRequest

This class represents the input request payload that contains a list of employees.

java

import java.util.List; public class EmployeeRequest { private List<Employee> employees; // Getters and Setters public List<Employee> getEmployees() { return employees; } public void setEmployees(List<Employee> employees) { this.employees = employees; } }

Employee

This class represents the individual employee data.

java

public class Employee { private String firstName; private String lastName; private String middleName; private String dateOfBirth; // Use String for simplicity; can use LocalDate or Date instead // Getters and Setters public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getMiddleName() { return middleName; } public void setMiddleName(String middleName) { this.middleName = middleName; } public String getDateOfBirth() { return dateOfBirth; } public void setDateOfBirth(String dateOfBirth) { this.dateOfBirth = dateOfBirth; } } 
 

How It Works

  1. Input: The API receives a JSON payload containing a list of employees with their details (firstNamelastNamemiddleName, and dateOfBirth).
  2. CSV Generation: It generates a CSV file with these details in memory.
  3. ZIP Compression: The CSV file is compressed into a ZIP archive.
  4. Response: The ZIP file is returned to the client with appropriate headers for file download.

  



Example Request

POST Request

http

POST /api/employee/generate Content-Type: application/json

Request Body

json

{ "employees": [ { "firstName": "John", "lastName": "Doe", "middleName": "A.", "dateOfBirth": "1985-03-15" }, { "firstName": "Jane", "lastName": "Smith", "middleName": "B.", "dateOfBirth": "1990-07-21" } ] }

Response

The API returns a employees.zip file containing the CSV file. The CSV will look like this:

csv

First Name,Last Name,Middle Name,Date of Birth John,Doe,A.,1985-03-15 Jane,Smith,B.,1990-07-21

 

1. Spring Boot Configuration Limits

Spring Boot uses Servlet multipart handling for file uploads and responses. The maximum file size is governed by the spring.servlet.multipart properties:

  • spring.servlet.multipart.max-file-size:

    • Limits the maximum size of a single uploaded file or generated response.
    • Default: 1MB.
  • spring.servlet.multipart.max-request-size:

    • Limits the total size of all files in a multipart request or response.
    • Default: 10MB.

Example Configuration in application.properties:

properties

spring.servlet.multipart.max-file-size=100MB spring.servlet.multipart.max-request-size=100MB

2. Server-Specific Limits

Your server configuration (e.g., Tomcat, Jetty) also enforces limits:

Tomcat:

  • server.tomcat.max-swallow-size: Specifies the maximum size of a request body or response.
    • Default: 2MB.
    • Set -1 for no limit.

Example:

properties

server.tomcat.max-swallow-size=-1

Other Servers:

  • Jetty, Undertow, or other embedded servers might have similar settings for request/response size limits. Check their documentation for details.

3. JVM Memory Limits

Your application must have enough memory to handle large files, especially for:

  • In-Memory Operations: When files are stored or manipulated in memory (e.g., CSV generation or zipping).
  • Heap Space: Ensure your JVM has adequate heap memory to avoid OutOfMemoryError.

JVM Options:

bash

-Xms512M -Xmx2G

This example sets the JVM to use 512MB initial memory and 2GB maximum memory.


4. Operating System Limits

The underlying operating system may impose limits on:

  • Temporary File Storage: For file uploads or in-memory buffers, check /tmp (or equivalent) storage space.
  • Maximum Open File Handles: Ensure you are not exceeding the file descriptor limits, particularly in high-concurrency scenarios.

5. Practical Limits for Download Responses

For downloading files (like a CSV zipped file):

  • StreamingResponseBody: Use it to stream large files directly to the response output stream, avoiding in-memory buffering.
  • This approach minimizes memory usage, allowing responses to exceed typical memory constraints.

6. Network Bandwidth

Large file downloads are also affected by:

  • Network bandwidth between the server and the client.
  • Timeouts due to prolonged transfers.

Ensure:

  • Timeouts are configured appropriately (server.connection-timeout in Spring Boot).
  • Clients can handle large file downloads.

Summary

  • Default Spring Boot limits are 1MB per file and 10MB per request.
  • You can increase these limits using properties like spring.servlet.multipart.max-file-size.
  • Use StreamingResponseBody for large file downloads to avoid memory bottlenecks.
  • Monitor JVM memory, server configurations, and OS limits to ensure performance and reliability.

If you are working with extremely large files (e.g., several GB), consider a more robust solution like:

  • Direct S3 File Downloads: Upload the file to AWS S3 and share a pre-signed URL.
  • Chunked File Downloads: Break the file into smaller chunks for transmission.

Wednesday, February 28, 2018

SpringBoot Externalization Part 1: Develop Externalization Server

Spring Boot lets you externalize your configuration so that you can work with the same application code in different environments. You can use properties files, YAML files, environment variables, and command-line arguments to externalize configuration. Property values can be injected directly into your beans by using the @Valueannotation, accessed through Spring’s Environment abstraction, or be bound to structured objects through @ConfigurationProperties.


This thread discusses connection of Spring Externalization Config Server and it caches the properties from Git Hub.

The properties files are in YAML format that are stored on GIT. 


 MindTelligentExternalizationApplication Server Configuration:



import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@EnableConfigServer
@SpringBootApplication
public class MindTelligentExternalizationApplication {

public static void main(String[] args) 
{
SpringApplication.run(MindTelligentExternalizationApplication .class, args);
}

}



Package the code with application.yml file:


server:
  port: 8888
spring:
  cloud:
    config:
      server:
        git:
          uri:  https://hsingh@github.com/ymlbranch
          searchPaths: src
          username: hsingh@MindTelligent.com

          password: XXXXXXXX 



Build the Code with Maven:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mindtelligent.github.server</groupId>
    <artifactId>MindTelligentIotConfigurationService</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.M8</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/libs-milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>


</project>



  • Once this is built. Run the JAR file with the command
java -jar MindTelligentIotConfigurationService-0.0.1-SNAPSHOT.jar

Log on to the browser and validate by:

http://hostname:8888/yml_file/default

You should be able to see the contents on the browser.



How IdP Groups Are Tied to Databricks Groups (Unity Catalog)

  🔗 How IdP Groups Are Tied to Databricks Groups (Unity Catalog) 🔑 Key Principle (Read This First) Databricks does NOT “map” IdP groups...