Skip to content

Instantly share code, notes, and snippets.

@mike-seger
Last active August 23, 2024 15:24
Show Gist options
  • Select an option

  • Save mike-seger/a1d5305391b327e80e51cf34b3a25f32 to your computer and use it in GitHub Desktop.

Select an option

Save mike-seger/a1d5305391b327e80e51cf34b3a25f32 to your computer and use it in GitHub Desktop.
Generic Queries

You:

can you provide a kotlin method to be used within a springboot application to check the existence of a table by name?

ChatGPT:

Certainly! Below is a Kotlin method that can be used within a Spring Boot application to check the existence of a table by name. This method uses JdbcTemplate to interact with the database.

import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.stereotype.Service

@Service
class DatabaseService(private val jdbcTemplate: JdbcTemplate) {

    fun doesTableExist(tableName: String): Boolean {
        val query = """
            SELECT COUNT(*) 
            FROM information_schema.tables 
            WHERE table_schema = 'public' 
            AND table_name = ?
        """
        val count = jdbcTemplate.queryForObject(query, arrayOf(tableName), Int::class.java)
        return count != null && count > 0
    }
}

Explanation:

  1. JdbcTemplate : This is a Spring JDBC helper class that simplifies database operations such as querying and updating.

  2. information_schema.tables : This is a standard SQL schema that stores information about all the tables in the database.

  3. table_schema : This filters the query to only consider tables within the specified schema (in this case, 'public'). You may need to adjust this according to your schema.

  4. table_name = ? : This is the placeholder for the table name, which is passed as a parameter.

  5. queryForObject : This method is used to execute the query and retrieve a single object (the count of tables with the specified name).

Usage:

You can inject this DatabaseService into any Spring component and call doesTableExist("your_table_name") to check if a table exists.

Example:

@RestController
@RequestMapping("/api")
class MyController(private val databaseService: DatabaseService) {

    @GetMapping("/check-table")
    fun checkTable(@RequestParam tableName: String): ResponseEntity<String> {
        val exists = databaseService.doesTableExist(tableName)
        return if (exists) {
            ResponseEntity.ok("Table $tableName exists.")
        } else {
            ResponseEntity.status(HttpStatus.NOT_FOUND).body("Table $tableName does not exist.")
        }
    }
}

This method and example controller allow you to verify the existence of a table in your database by sending a GET request to /api/check-table?tableName=your_table_name.

You:

does this work with all DBs?

ChatGPT:

The provided method leverages information_schema.tables, which is a standard part of the SQL specification and is supported by many relational databases. However, the behavior can vary slightly between different database systems, especially regarding the schema name and some specifics of the SQL dialect. Here's a breakdown of how it works with some common databases:

  1. PostgreSQL :
  • The query should work as expected. The schema name public is commonly used, but you should adjust this if your tables are in a different schema.
  1. MySQL :
  • The query should also work in MySQL. However, MySQL uses the concept of a database as a schema, so table_schema would typically be the name of the database.
  1. SQL Server :
  • SQL Server supports information_schema.tables, but the schema name public is not applicable. You might need to use dbo or another specific schema name.
  1. Oracle :
  • Oracle supports information_schema under a different context. You might need to adjust the query to use USER_TABLES or ALL_TABLES instead.
  1. SQLite :
  • SQLite doesn't have schemas, so information_schema won't be available. You might need to check table existence by querying the sqlite_master table instead.

Given these variations, the following adjustments can be made for different databases:

Generic Method

fun doesTableExist(tableName: String, schemaName: String = "public"): Boolean {
    val query = """
        SELECT COUNT(*) 
        FROM information_schema.tables 
        WHERE table_schema = ? 
        AND table_name = ?
    """
    val count = jdbcTemplate.queryForObject(query, arrayOf(schemaName, tableName), Int::class.java)
    return count != null && count > 0
}

Adjustments for Specific Databases

  • For MySQL :
val query = """
    SELECT COUNT(*) 
    FROM information_schema.tables 
    WHERE table_schema = DATABASE() 
    AND table_name = ?
"""
  • For SQL Server :
val query = """
    SELECT COUNT(*) 
    FROM information_schema.tables 
    WHERE table_catalog = ? 
    AND table_schema = 'dbo' 
    AND table_name = ?
"""

Replace table_catalog with the database name or pass it as a parameter.

  • For Oracle :
val query = """
    SELECT COUNT(*) 
    FROM all_tables 
    WHERE owner = ? 
    AND table_name = ?
"""
  • For SQLite :
val query = """
    SELECT COUNT(*) 
    FROM sqlite_master 
    WHERE type = 'table' 
    AND name = ?
"""

Considerations

  • Ensure the schema name (public, dbo, etc.) is appropriate for your database.

  • Be mindful of case sensitivity, as some databases like PostgreSQL treat identifiers as case-insensitive unless quoted.

Alternative Approach

If you plan to support multiple databases, it might be useful to create a database-specific implementation of the doesTableExist method using Spring's @ConditionalOnProperty or similar mechanisms to switch between database dialects. Overall, while the method is generally applicable, you should adapt the query slightly depending on the specific database you're using in your Spring Boot application.

You:

can you also provide a generic method to list all table names?

ChatGPT:

Certainly! Below is a Kotlin method that provides a generic approach to list all table names in a database. This method uses JdbcTemplate and is designed to be adaptable across different relational databases.

Generic Method to List All Table Names

import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.stereotype.Service

@Service
class DatabaseService(private val jdbcTemplate: JdbcTemplate) {

    fun listAllTableNames(schemaName: String = "public"): List<String> {
        val query = """
            SELECT table_name 
            FROM information_schema.tables 
            WHERE table_schema = ?
            AND table_type = 'BASE TABLE'
        """
        return jdbcTemplate.queryForList(query, arrayOf(schemaName), String::class.java)
    }
}

Adjustments for Specific Databases

  1. PostgreSQL (default):
  • The method should work directly as provided. PostgreSQL uses public as the default schema.
  1. MySQL :
fun listAllTableNames(): List<String> {
    val query = """
        SELECT table_name 
        FROM information_schema.tables 
        WHERE table_schema = DATABASE()
        AND table_type = 'BASE TABLE'
    """
    return jdbcTemplate.queryForList(query, String::class.java)
}
  • MySQL uses the concept of a database as a schema. The DATABASE() function retrieves the current database name.
  1. SQL Server :
fun listAllTableNames(schemaName: String = "dbo"): List<String> {
    val query = """
        SELECT table_name 
        FROM information_schema.tables 
        WHERE table_schema = ?
        AND table_type = 'BASE TABLE'
    """
    return jdbcTemplate.queryForList(query, arrayOf(schemaName), String::class.java)
}
  • SQL Server typically uses dbo as the default schema.
  1. Oracle :
fun listAllTableNames(owner: String): List<String> {
    val query = """
        SELECT table_name 
        FROM all_tables 
        WHERE owner = ?
    """
    return jdbcTemplate.queryForList(query, arrayOf(owner), String::class.java)
}
  • Oracle uses ALL_TABLES to list tables that the user has access to. The owner parameter corresponds to the schema name.
  1. SQLite :
fun listAllTableNames(): List<String> {
    val query = """
        SELECT name 
        FROM sqlite_master 
        WHERE type = 'table'
    """
    return jdbcTemplate.queryForList(query, String::class.java)
}
  • SQLite stores table metadata in sqlite_master, which can be queried directly without a schema concept.

Explanation:

  • JdbcTemplate : This Spring class is used for executing SQL queries.

  • table_schema : Filters the tables by schema name. This is essential in databases where multiple schemas exist.

  • table_type = 'BASE TABLE' : Ensures that only physical tables (and not views) are listed. This is applicable for databases like PostgreSQL, MySQL, and SQL Server.

  • schemaName : This parameter allows specifying the schema to list tables from. It's optional and defaults to 'public' for PostgreSQL.

Usage:

You can inject this DatabaseService into any Spring component and call listAllTableNames() to retrieve a list of table names.

Example:

@RestController
@RequestMapping("/api")
class MyController(private val databaseService: DatabaseService) {

    @GetMapping("/tables")
    fun getTables(@RequestParam(required = false) schemaName: String?): ResponseEntity<List<String>> {
        val tables = if (schemaName != null) {
            databaseService.listAllTableNames(schemaName)
        } else {
            databaseService.listAllTableNames()
        }
        return ResponseEntity.ok(tables)
    }
}

This example controller provides an endpoint /api/tables that lists all tables in the specified schema (or the default schema if none is specified). By adjusting the method based on your specific database, you can create a robust, cross-database solution for listing table names in your Spring Boot application.

@Repository
class MyRepo(
    @PersistenceContext
    private val entityManager: EntityManager,
    private val databaseService: DatabaseService 
) {
    fun doSomething() {
        if(databaseService.tableExists()) {
         ...
        }
    }
}

@Service
class DatabaseService(private val dataSource: DataSource) {

    fun tableNames(schemaName: String? = null): List<String> {
        val tableNames = mutableListOf<String>()
        
        dataSource.connection.use { connection ->
            val metaData: Connection = connection.metaData
            val schema = schemaName ?: connection.schema
            
            // Retrieve the table names
            val resultSet = metaData.getTables(null, schema, "%", arrayOf("TABLE"))
            
            while (resultSet.next()) {
                val tableName = resultSet.getString("TABLE_NAME")
                tableNames.add(tableName)
            }
        }
        
        return tableNames
    }
    
    fun tableExists(tableName: String, schemaName: String? = null) =
        tableNames(schemaName).any { it.equals(tableName, ignoreCase = true) }
}
SELECT COUNT(*)
FROM your_table
WHERE NOT (
    DBMS_LOB.SUBSTR(FIELD, 1, 1) IN ('{', '[', '"') AND 
    (
        DBMS_LOB.SUBSTR(FIELD, 1, 1) = '{' AND DBMS_LOB.SUBSTR(FIELD, 1, DBMS_LOB.GETLENGTH(FIELD)) = '}'
        OR
        DBMS_LOB.SUBSTR(FIELD, 1, 1) = '[' AND DBMS_LOB.SUBSTR(FIELD, 1, DBMS_LOB.GETLENGTH(FIELD)) = ']'
        OR
        DBMS_LOB.SUBSTR(FIELD, 1, 1) = '"' AND DBMS_LOB.SUBSTR(FIELD, 1, DBMS_LOB.GETLENGTH(FIELD)) = '"'
    )
    OR
    REGEXP_LIKE(TRIM(FIELD), '^-?\d+(\.\d+)?$')
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment