Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Replaced DataSource with CsvDataSource, removed legacy examples, …
…and added new Spring integration demos

This commit deprecates the legacy `@DataSource` annotation in favor of the more specific `@CsvDataSource`. It removes outdated example files and introduces new detailed Spring integration examples demonstrating annotation-based DataFrame initialization, including `CsvDataSource_with_Application_Context` and `CsvDataSource_with_Configuration`. Adjustments also include sample data reorganization and updates to tests for compatibility.
  • Loading branch information
zaleslaw committed Aug 22, 2025
commit 3fde0e254b79b35fa882d320fdadec91b2b9ef58
6 changes: 3 additions & 3 deletions dataframe-spring/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ dependencies {
api(projects.dataframeJdbc)

// Spring dependencies
implementation("org.springframework:spring-context:6.0.0")
implementation("org.springframework:spring-beans:6.0.0")
implementation("org.springframework:spring-context:6.2.7")
implementation("org.springframework:spring-beans:6.2.7")
implementation(libs.kotlin.reflect)

// Test dependencies
testImplementation("org.springframework:spring-test:6.0.0")
testImplementation("org.springframework:spring-test:6.2.7")
testImplementation(libs.junit.jupiter)
testImplementation(libs.kotlin.test)
testImplementation(libs.kotestAssertions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import org.springframework.context.support.StaticApplicationContext
* - @JsonDataSource - for JSON files
* - @ArrowDataSource - for Arrow/Parquet/Feather files
* - @JdbcDataSource - for database tables/queries
* - @DataSource - legacy CSV annotation (deprecated)
*
* Usage:
* ```kotlin
Expand Down Expand Up @@ -56,8 +55,7 @@ class DataFramePostProcessor : BeanPostProcessor, ApplicationContextAware {
CsvDataSource::class.java to CsvDataSourceProcessor(),
JsonDataSource::class.java to JsonDataSourceProcessor(),
ArrowDataSource::class.java to ArrowDataSourceProcessor(),
JdbcDataSource::class.java to JdbcDataSourceProcessor(),
DataSource::class.java to LegacyCsvDataSourceProcessor() // For backward compatibility
JdbcDataSource::class.java to JdbcDataSourceProcessor()
)

override fun setApplicationContext(applicationContext: ApplicationContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ annotation class CsvDataSource(
val file: String,
val delimiter: Char = ',',
val header: Boolean = true
)
)
Original file line number Diff line number Diff line change
@@ -1,26 +0,0 @@
package org.jetbrains.kotlinx.dataframe.spring.annotations

/**
* Legacy annotation to mark DataFrame fields/properties for CSV data loading.
*
* @deprecated Use @CsvDataSource instead for CSV files
*
* @param csvFile The path to the CSV file to read from
* @param delimiter The delimiter character to use for CSV parsing (default: ',')
* @param header Whether the first row contains column headers (default: true)
*
* @see CsvDataSource
* @see DataFramePostProcessor
*/
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Deprecated(
"Use @CsvDataSource instead",
ReplaceWith("CsvDataSource(file = csvFile, delimiter = delimiter, header = header)")
)
annotation class DataSource(
val csvFile: String,
val delimiter: Char = ',',
val header: Boolean = true
)
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import org.jetbrains.kotlinx.dataframe.io.JSON
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class JsonDataSource(
val file: String,
val keyValuePaths: Array<String> = [],
val typeClashTactic: JSON.TypeClashTactic = JSON.TypeClashTactic.ARRAY_AND_VALUE_COLUMNS,
val unifyNumbers: Boolean = true
)
open annotation class JsonDataSource(
open val file: String,
open val keyValuePaths: Array<String> = [],
open val typeClashTactic: JSON.TypeClashTactic = JSON.TypeClashTactic.ARRAY_AND_VALUE_COLUMNS,
open val unifyNumbers: Boolean = true
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package org.jetbrains.kotlinx.dataframe.spring.examples

import org.jetbrains.kotlinx.dataframe.spring.DataFramePostProcessor
import java.io.File

private const val CUSTOMERS_CSV = "customers.csv"
private const val SALES_CSV = "sales.csv"

/**
* The entry point of the application.
*
* This method demonstrates how a `DataFramePostProcessor` processes Spring beans
* that are annotated with custom `@CsvDataSource` annotations and loads DataFrames
* from CSV files. The method performs the following actions:
*
* 1. Creates sample CSV files containing customer and sales data.
* 2. Initializes a `DataFramePostProcessor` to handle data source annotations.
* 3. Processes the annotations for a Spring service (`ExampleDataService`) to load
* DataFrames from the sample CSV files.
* 4. Outputs the results of the loaded DataFrames, including row count and column names.
* 5. Executes business logic from the service to print customer and sales counts.
* 6. Cleans up the generated sample CSV files.
*/
fun main() {
// Create sample CSV files
createSampleData()

try {
println("1. Creating DataFramePostProcessor...")
val processor = DataFramePostProcessor()

println("2. Processing @CsvDataSource annotations...")
val service = ExampleDataService()
processor.postProcessBeforeInitialization(service, "exampleService")

println("3. DataFrame loaded successfully!")
println(" - CSV file: data.csv")
println(" - Rows loaded: ${service.customerData.rowsCount()}")
println(" - Columns: ${service.customerData.columnNames()}")

println("4. Running business logic...")
service.printCustomerCount()
service.printSalesCount()

println("✓ @CsvDataSource annotation processing completed successfully!")

} catch (e: Exception) {
println("✗ Error processing @DataSource annotations: ${e.message}")
e.printStackTrace()
} finally {
// Clean up sample files
cleanupSampleData()
}
}

private fun createSampleData() {
// Create customer data
File(CUSTOMERS_CSV).writeText("""
id,name,email,age
1,John Doe,[email protected],28
2,Jane Smith,[email protected],32
3,Bob Johnson,[email protected],25
4,Alice Brown,[email protected],30
""".trimIndent())

// Create sales data with semicolon delimiter
File(SALES_CSV).writeText("""
sale_id;customer_id;amount;date
1;1;150.00;2023-01-15
2;2;200.50;2023-01-16
3;1;75.25;2023-01-17
4;3;300.00;2023-01-18
""".trimIndent())
}

private fun cleanupSampleData() {
File(CUSTOMERS_CSV).delete()
File(SALES_CSV).delete()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package org.jetbrains.kotlinx.dataframe.spring.examples

import org.jetbrains.kotlinx.dataframe.DataFrame
import org.jetbrains.kotlinx.dataframe.annotations.DataSchema
import org.jetbrains.kotlinx.dataframe.spring.DataFramePostProcessor
import org.jetbrains.kotlinx.dataframe.spring.annotations.CsvDataSource
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import java.io.File

private const val CUSTOMERS_CSV = "customers.csv"
private const val SALES_CSV = "sales.csv"

// Define the data schema
@DataSchema
interface CustomerRow {
val id: Int
val name: String
val email: String
val age: Int
}

@DataSchema
interface SalesRow {
val saleId: Int
val customerId: Int
val amount: Double
val date: String
}

class ExampleDataService {
@CsvDataSource(file = CUSTOMERS_CSV)
lateinit var customerData: DataFrame<CustomerRow>

@CsvDataSource(file = SALES_CSV, delimiter = ';')
lateinit var salesData: DataFrame<SalesRow>

fun printCustomerCount() {
println("Number of customers: ${customerData.rowsCount()}")
}

fun printSalesCount() {
println("Number of sales: ${salesData.rowsCount()}")
}
}

/**
* Entry point for the application. This method demonstrates the use of a Spring context
* with a custom annotation processor to load and process CSV data into DataFrames.
*
* The method performs the following steps:
* 1. Generates sample customer and sales CSV files for demonstration purposes.
* 2. Initializes a Spring application context and registers the required components, including
* DataFramePostProcessor and ExampleDataService.
* 3. Loads the CSV data into DataFrames by leveraging the @CsvDataSource annotation.
* 4. Outputs information about the loaded data, such as file name, number of rows, and column names.
* 5. Executes example business logic using the ExampleDataService, such as printing customer and
* sales counts.
* 6. Logs any errors encountered during processing and ensures cleanup of generated sample files.
*/
fun main() {
// Create sample CSV files
createSampleData()

try {
println("1. Bootstrapping Spring context...")
val ctx = AnnotationConfigApplicationContext().apply {
register(DataFramePostProcessor::class.java)
register(ExampleDataService::class.java)
refresh()
}

println("2. Getting MyDataService bean from context...")
val myDataService = ctx.getBean(ExampleDataService::class.java)

println("3. DataFrame loaded successfully!")
println(" - CSV file: data.csv")
println(" - Rows loaded: ${myDataService.customerData.rowsCount()}")
println(" - Columns: ${myDataService.customerData.columnNames()}")

println("4. Running business logic...")
myDataService.printCustomerCount()
myDataService.printSalesCount()

println("✓ @CsvDataSource annotation processing completed successfully!")

} catch (e: Exception) {
println("✗ Error processing @DataSource annotations: ${e.message}")
e.printStackTrace()
} finally {
// Clean up sample files
cleanupSampleData()
}
}

private fun createSampleData() {
// Create customer data
File(CUSTOMERS_CSV).writeText("""
id,name,email,age
1,John Doe,[email protected],28
2,Jane Smith,[email protected],32
3,Bob Johnson,[email protected],25
4,Alice Brown,[email protected],30
""".trimIndent())

// Create sales data with semicolon delimiter
File(SALES_CSV).writeText("""
sale_id;customer_id;amount;date
1;1;150.00;2023-01-15
2;2;200.50;2023-01-16
3;1;75.25;2023-01-17
4;3;300.00;2023-01-18
""".trimIndent())
}

private fun cleanupSampleData() {
File(CUSTOMERS_CSV).delete()
File(SALES_CSV).delete()
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,13 @@
package org.jetbrains.kotlinx.dataframe.spring.examples

import org.jetbrains.kotlinx.dataframe.DataFrame
import org.jetbrains.kotlinx.dataframe.annotations.DataSchema
import org.jetbrains.kotlinx.dataframe.spring.DataFramePostProcessor
import org.jetbrains.kotlinx.dataframe.spring.annotations.DataSource
import org.springframework.beans.factory.config.BeanDefinition
import org.springframework.beans.factory.config.BeanFactoryPostProcessor
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
import org.jetbrains.kotlinx.dataframe.spring.annotations.CsvDataSource
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Scope
import org.springframework.stereotype.Component
import java.io.File

// Define the data schema
@DataSchema
interface CustomerRow {
val id: Int
val name: String
val email: String
val age: Int
}

@DataSchema
interface SalesRow {
val saleId: Int
val customerId: Int
val amount: Double
val date: String
}

/**
* Example Spring service that uses @DataSource annotation
Expand All @@ -37,10 +16,10 @@ interface SalesRow {
@Component
class DataAnalysisService {

@DataSource(csvFile = "customers.csv")
@CsvDataSource(file = "customers.csv")
lateinit var customers: DataFrame<CustomerRow>

@DataSource(csvFile = "sales.csv", delimiter = ';')
@CsvDataSource(file = "sales.csv", delimiter = ';')
lateinit var sales: DataFrame<SalesRow>

fun analyzeCustomerData() {
Expand Down Expand Up @@ -87,8 +66,20 @@ open class DataFrameConfiguration {
}
}


/**
* Example demonstrating the complete Spring integration
* Entry point for the DataFrame Spring Integration Example application.
*
* This method demonstrates a mock integration of Kotlin DataFrames with a
* Spring-like lifecycle. It performs the following tasks:
*
* 1. Creates sample data files (e.g., CSV files) to simulate data sources.
* 2. Initializes a DataFramePostProcessor to mimic Spring's BeanPostProcessor functionality.
* 3. Simulates the creation and initialization of a Spring bean (DataAnalysisService).
* 4. Processes mock `@DataSource` annotations to load data into DataFrame properties.
* 5. Executes a sample data analysis and generates a combined report.
* 6. Highlights key features of declarative data integration using annotations.
* 7. Cleans up the sample data files after execution.
*/
fun main() {
println("DataFrame Spring Integration Example")
Expand All @@ -113,7 +104,7 @@ fun main() {

println("\n✓ Spring-style DataFrame integration completed successfully!")
println("\nThis demonstrates:")
println("- @DataSource annotation for declarative CSV loading")
println("- @CsvDataSource annotation for declarative CSV loading")
println("- Automatic DataFrame population during bean initialization")
println("- Support for custom delimiters")
println("- Integration with Spring's dependency injection lifecycle")
Expand Down
Loading