code: def spark_read_from_jdbc(
      spark_session: SparkSession,
      url: str,
      user: str,
      password: str,
      metastore_table: str,
      jdbc_table: str,
      driver: Any,
      save_mode: str,
      save_format: str,
      fetch_size: int,
      num_partitions: int,
      partition_column: str,
      lower_bound: str,
      upper_bound: str,
  ) -> None:
      """Transfer data from JDBC source to Spark."""
      # first set common options
      reader = set_common_options(
          spark_session.read, url, jdbc_table, user, password, driver
      )
  
      # now set specific read options
      if fetch_size:
          reader = reader.option("fetchsize", fetch_size)
      if num_partitions:
          reader = reader.option("numPartitions", num_partitions)
      if partition_column and lower_bound and upper_bound:
          reader = (
              reader.option("partitionColumn", partition_column)
              .option("lowerBound", lower_bound)
              .option("upperBound", upper_bound)
          )
  
      reader.load().write.saveAsTable(
          metastore_table, format=save_format, mode=save_mode
      )
  
file_path: airflow/airflow/providers/apache/spark/hooks/spark_jdbc_script.py
imports: from __future__ import annotations
  import argparse
  from typing import Any
  from pyspark.sql import SparkSession
label_desc: Use image node_id 3 for calling a global function with example usage: spark_read_from_jdbc(spark_session, url, user, password, metastore_table, jdbc_table, driver, save_mode, save_format, fetch_size, num_partitions, partition_column, lower_bound, upper_bound) without return types