How to Connect to BigQuery from Java

6 min readBigQuery

BigQuery is not a database you open a socket to. There is no host or port. You authenticate against Google Cloud, then issue queries over HTTPS through Google's API. For Java that means the official google-cloud-bigquery client library, not a JDBC driver. A Simba JDBC driver exists and matters when a legacy tool demands a JDBC interface, but for application code the client library is the right call. This guide covers authentication, running queries, parameters, cost controls, the location trap, and the errors you will actually hit.

Client library vs JDBC driver

OptionWhen to use
google-cloud-bigquery (client library)Application code. Idiomatic Java, maintained by Google, full feature access.
Simba JDBC driver / Google-developed JDBC driverWhen a tool you do not control (a BI connector, an ETL framework) speaks only JDBC.

For your own Java code, reach for the client library. The JDBC route exists to plug BigQuery into JDBC-only tooling, not because it is a better fit for application development. This guide uses the client library.

Adding the dependency

Use the Google Cloud libraries BOM so the BigQuery version stays aligned with its transitive Google dependencies, then declare the artifact without a version.

Maven:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>libraries-bom</artifactId>
      <version>26.84.0</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
 
<dependencies>
  <dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-bigquery</artifactId>
  </dependency>
</dependencies>

Gradle:

implementation platform('com.google.cloud:libraries-bom:26.84.0')
implementation 'com.google.cloud:google-cloud-bigquery'

If you prefer to pin directly without the BOM, the standalone artifact is at 2.67.0 (as of June 2026). The BOM is the safer default because it prevents version skew across the Google client stack.

Authentication

There is no password in a connection string. The client authenticates through Application Default Credentials (ADC), which resolves credentials in this order:

  1. The GOOGLE_APPLICATION_CREDENTIALS environment variable, pointing at a service-account key JSON file.
  2. Credentials from gcloud auth application-default login (local development).
  3. The attached service account, when running on Google Cloud (Compute Engine, Cloud Run, GKE, etc.).

On Google Cloud infrastructure, option 3 means you usually need no key file at all: the runtime's service account is used automatically. For local development, gcloud auth application-default login is the cleanest path because it avoids a long-lived key file on disk.

import com.google.cloud.bigquery.*;
 
// ADC is picked up automatically; no credentials in code.
BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

To pin an explicit billing project rather than relying on the ADC default:

BigQuery bigquery = BigQueryOptions.newBuilder()
    .setProjectId("my-billing-project")
    .build()
    .getService();

Note the distinction between the billing project (who pays for the query) and the data project (where the tables live). They are often the same, but the project you set here is the one billed; the tables you reference can sit in another project via fully qualified project.dataset.table names.

Running a query

import com.google.cloud.bigquery.*;
 
public class BigQueryExample {
    public static void main(String[] args) throws InterruptedException {
        BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();
 
        String query =
            "SELECT name, SUM(number) AS total " +
            "FROM `bigquery-public-data.usa_names.usa_1910_2013` " +
            "WHERE state = 'TX' " +
            "GROUP BY name ORDER BY total DESC LIMIT 10";
 
        QueryJobConfiguration config = QueryJobConfiguration.newBuilder(query).build();
 
        TableResult result = bigquery.query(config);
 
        for (FieldValueList row : result.iterateAll()) {
            String name = row.get("name").getStringValue();
            long total = row.get("total").getLongValue();
            System.out.println(name + ": " + total);
        }
    }
}

bigquery.query() runs the job and blocks until results are ready. result.iterateAll() pages through the full result set transparently, fetching more pages from the API as you iterate, so you do not have to manage pagination by hand.

Parameterized queries

Build queries from user input with named parameters, never string concatenation. Query parameters require Standard SQL (the default).

String query =
    "SELECT name, SUM(number) AS total " +
    "FROM `bigquery-public-data.usa_names.usa_1910_2013` " +
    "WHERE state = @state AND name = @name " +
    "GROUP BY name";
 
QueryJobConfiguration config = QueryJobConfiguration.newBuilder(query)
    .addNamedParameter("state", QueryParameterValue.string("TX"))
    .addNamedParameter("name", QueryParameterValue.string("Emma"))
    .build();
 
TableResult result = bigquery.query(config);

Positional parameters (?) are also supported via addPositionalParameter, but named parameters read better and are harder to misorder.

Cost controls

BigQuery bills by bytes scanned, so a careless query against a large table can be expensive. Two guardrails are worth wiring in from the start.

A dry run tells you how many bytes a query would scan without running it or incurring cost:

QueryJobConfiguration dryRun = QueryJobConfiguration.newBuilder(query)
    .setDryRun(true)
    .setUseQueryCache(false)
    .build();
 
Job job = bigquery.create(JobInfo.of(dryRun));
JobStatistics.QueryStatistics stats = job.getStatistics();
System.out.println("Would scan: " + stats.getTotalBytesProcessed() + " bytes");

A hard byte cap makes a query fail rather than run away with your budget:

QueryJobConfiguration config = QueryJobConfiguration.newBuilder(query)
    .setMaximumBytesBilled(1_000_000_000L) // 1 GB ceiling
    .build();

If the query would scan more than the cap, it errors out instead of billing you. For exploratory work against unfamiliar tables, set this.

The location trap

Datasets live in a location (a region or multi-region like US or EU). If you query a dataset in EU but the job defaults to US, BigQuery reports that the table is not found, which looks like a permissions or typo problem but is actually a location mismatch. Set the location explicitly when your data is not in the default:

QueryJobConfiguration config = QueryJobConfiguration.newBuilder(query).build();
JobId jobId = JobId.newBuilder().setLocation("EU").build();
TableResult result = bigquery.query(config, jobId);

Errors you will actually hit

SymptomLikely causeFix
Application Default Credentials not foundADC not configuredRun gcloud auth application-default login, or set GOOGLE_APPLICATION_CREDENTIALS
Not found: Table ... (but the table exists)Job ran in the wrong locationSet the dataset's location on the JobId
Access Denied: BigQuery ...Service account lacks rolesGrant roles/bigquery.dataViewer and roles/bigquery.jobUser
Query exceeded limit for bytes billedQuery scanned more than maximumBytesBilledNarrow the query, or raise the cap deliberately
403 ... has not enabled BigQuery APIAPI disabled on the projectEnable the BigQuery API in the Cloud Console
400 ... billing has not been enabledNo billing account on the projectAttach a billing account

The "table not found" case is the one that wastes the most time, because the error wording points you at the table name when the real fix is the location.

A note on querying after you connect

Once authentication works, the day-to-day question is the SQL: confirming a table's schema, eyeballing a result set, checking a row count before a big scan. You can do that from the bq command-line tool, the Cloud Console, a GUI (see Best BigQuery GUI Clients), or with Mako. For loading data, see Import CSV to BigQuery and Import JSON to BigQuery. If you are weighing engines, Snowflake vs BigQuery and ClickHouse vs BigQuery cover the tradeoffs.

Version notes (as of June 2026): google-cloud-bigquery is at 2.67.x, managed through libraries-bom 26.84.x. The client targets current LTS Java releases.

Mako connects to BigQuery with AI-powered autocomplete for writing queries. Try it free at mako.ai.

Mako — open source

Skip the terminal. Use Mako.

Connect your database, write queries with AI assistance, and import/export data in clicks. Free to start.